ไฟล์ .gitignore: วิธีไม่ให้ Git ติดตามไฟล์และนำไฟล์ที่เพิ่มแล้วออก
ไฟล์ .gitignore ที่รากของรีโพซิทอรีระบุรูปแบบของไฟล์และโฟลเดอร์ที่ Git จะไม่ติดตาม เช่น dependencies ไฟล์บิลด์ ล็อก และความลับ มีผลเฉพาะกับไฟล์ที่ยังไม่ถูกติดตาม ดังนั้นไฟล์ที่คอมมิตไปแล้วต้องนำออกจากดัชนีด้วยคำสั่ง git rm --cached
git ignore คือวิธีบอก Git ว่า "อย่าแตะไฟล์เหล่านี้ อย่าแสดงใน git status และอย่าเพิ่มผ่าน git add ." กฎเขียนไว้ในไฟล์ข้อความชื่อ .gitignore ซึ่งถูกคอมมิตไปพร้อมโปรเจกต์และใช้ได้กับทั้งทีม ประเด็นสำคัญคือ การละเว้นมีผลเฉพาะกับไฟล์ที่ Git ยังไม่ได้ติดตาม
สิ่งที่มักใส่ใน .gitignore
- Dependencies:
node_modules/,vendor/,.venv/เพราะตัวจัดการแพ็กเกจกู้คืนได้เสมอ - ผลลัพธ์จากการบิลด์:
dist/,build/,*.class,__pycache__/ - ล็อกและไฟล์ชั่วคราว:
*.log,tmp/,*.swp - ความลับและการตั้งค่าในเครื่อง:
.env, คีย์, คอนฟิกส่วนตัว - ไฟล์ขยะจากระบบปฏิบัติการและเอดิเตอร์:
.DS_Store,Thumbs.db,.idea/แม้ว่าควรเก็บไว้ในการละเว้นทั่วทั้งเครื่อง ซึ่งจะอธิบายด้านล่าง
กฎง่าย ๆ: ในรีโพซิทอรีควรมีสิ่งที่คนเขียน ไม่ใช่สิ่งที่โปรแกรมสร้างหรือขึ้นกับเครื่องใดเครื่องหนึ่ง
วิธีสร้าง .gitignore
เป็นไฟล์ข้อความธรรมดาที่รากของรีโพซิทอรี สร้าง ใส่รูปแบบทีละบรรทัด และคอมมิต:
cat > .gitignore <<'EOF'
# dependencies
node_modules/
# ไฟล์บิลด์
dist/
# ล็อก
*.log
# ความลับ
.env
EOF
git add .gitignore
git commit -m "chore: เพิ่ม .gitignore"
เทมเพลตพร้อมใช้สำหรับภาษาและเฟรมเวิร์กยอดนิยมมีอยู่ในรีโพซิทอรี github/gitignore GitHub และ GitLab ก็ให้เลือกเทมเพลตเมื่อสร้างโปรเจกต์ ใช้เป็นพื้นฐานแล้วตัดส่วนที่ไม่จำเป็นออก
ไวยากรณ์ของรูปแบบใน .gitignore
| รูปแบบ | สิ่งที่ละเว้น |
|---|---|
*.log | ไฟล์นามสกุล .log ในทุกโฟลเดอร์ |
build/ | โฟลเดอร์ build ในทุกระดับ (เครื่องหมายทับท้ายหมายถึงเฉพาะโฟลเดอร์) |
/build | เฉพาะ build ที่รากเท่านั้น (ทับนำหน้าผูกกับโฟลเดอร์ของ .gitignore) |
docs/*.pdf | pdf ที่อยู่ใน docs โดยตรง แต่ไม่รวม docs/old/ |
docs/**/*.pdf | pdf ใน docs และโฟลเดอร์ย่อยทั้งหมด |
**/temp | temp ในทุกโฟลเดอร์ |
!keep.log | ข้อยกเว้น: ไม่ละเว้น keep.log |
file?.txt | ? คืออักขระใดก็ได้หนึ่งตัว |
[ab].txt | a.txt หรือ b.txt |
# คอมเมนต์ | บรรทัดคอมเมนต์ |
ข้อควรรู้: รูปแบบที่ไม่มีทับอยู่ข้างใน (config.json) ทำงานกับทุกระดับ ส่วนรูปแบบที่มีทับนำหน้าหรือตรงกลาง (/config.json, src/config.json) จะอ้างอิงจากโฟลเดอร์ที่มีไฟล์ .gitignore นี้
ข้อยกเว้นด้วยเครื่องหมายอัศเจรีย์
*.log
!important.log
กับดัก: ไม่สามารถ "คืน" ไฟล์ได้หากโฟลเดอร์แม่ถูกละเว้น Git ไม่เข้าไปในโฟลเดอร์ที่ถูกละเว้น จึงใช้ไม่ได้:
logs/
!logs/keep.txt # ไม่ทำงาน
วิธีที่ถูกต้องคือละเว้นเนื้อหาแทนโฟลเดอร์:
logs/*
!logs/keep.txt
เทคนิคเดียวกันใช้เก็บโฟลเดอร์ว่างไว้ในรีโพซิทอรี เนื่องจาก Git ไม่เก็บไดเรกทอรีว่าง จึงใส่ไฟล์ .gitkeep ไว้ข้างใน และอนุญาตเฉพาะไฟล์นี้
ตั้งค่าการละเว้นที่อื่นได้อีก
| ตำแหน่ง | มีผลกับ | เข้ารีโพซิทอรีหรือไม่ |
|---|---|---|
.gitignore ที่รากและโฟลเดอร์ย่อย | ทั้งทีม | ใช่ |
.git/info/exclude | เฉพาะโคลนนี้ | ไม่ |
ไฟล์ส่วนกลาง core.excludesFile | รีโพซิทอรีทั้งหมดของคุณ | ไม่ |
ขยะของระบบปฏิบัติการและ IDE ควรนำไปไว้ในการละเว้นส่วนกลางเพื่อไม่ให้ .gitignore ทั่วไปรก:
git config --global core.excludesFile ~/.gitignore_global
echo ".DS_Store" >> ~/.gitignore_global
echo ".idea/" >> ~/.gitignore_global
หากไม่ได้ตั้ง core.excludesFile Git จะอ่าน ~/.config/git/ignore หากไฟล์นั้นมีอยู่
ทำไม .gitignore ถึงไม่ทำงาน
ข้อร้องเรียนที่พบบ่อยที่สุดคือ "เพิ่มไฟล์ลงใน .gitignore แล้ว แต่ Git ยังแสดงการเปลี่ยนแปลง" สาเหตุเกือบทุกครั้งคือไฟล์นั้นถูกติดตามอยู่แล้ว การละเว้นไม่มีผลกับไฟล์ที่เคยเข้าสู่คอมมิต
หากต้องการตรวจว่ากฎใดทำงาน (หรือทำไมไม่ทำงาน) ใช้ git check-ignore:
git check-ignore -v logs/app.log
# .gitignore:3:*.log logs/app.log
ผลลัพธ์แสดงไฟล์และบรรทัดของกฎ หากไม่มีผลลัพธ์ แปลว่าไฟล์ไม่ถูกละเว้น อีกสาเหตุหนึ่งของความล้มเหลวคือมีช่องว่างท้ายบรรทัดของรูปแบบ หรือไฟล์มี encoding ผิด (เช่น UTF-16 เมื่อสร้างผ่าน PowerShell)
นำไฟล์ที่คอมมิตแล้วออกจากรีโพซิทอรี: git rm --cached
คำสั่ง git rm ลบไฟล์ออกจากดัชนีและจากดิสก์ เมื่อใช้แฟล็ก --cached จะลบออกจากดัชนีเท่านั้น ไฟล์ยังอยู่ในเครื่องของคุณแต่หยุดถูกติดตาม
echo ".env" >> .gitignore
git rm --cached .env
git commit -m "chore: .env ไม่ถูกติดตามแล้ว"
git push
สำหรับโฟลเดอร์ต้องใช้ -r:
git rm -r --cached node_modules/
หากมีกฎจำนวนมากและไม่แน่ใจว่าอะไรเข้ารีโพซิทอรีไปแล้ว สามารถสร้างดัชนีใหม่ทั้งหมดได้: git rm -r --cached . จากนั้น git add . และคอมมิต ก่อนคอมมิตให้ตรวจ git status จะเห็นไฟล์ที่ถูกนำออกจากดัชนี
คำเตือน: ความลับยังอยู่ในประวัติ
git rm --cached นำไฟล์ออกจากคอมมิตถัดไป แต่ไม่ได้นำออกจากคอมมิตเดิม หากรหัสผ่านหรือโทเคนหลุดเข้ารีโพซิทอรี มันจะเข้าถึงได้ผ่านประวัติสำหรับทุกคนที่มีโคลน ต้องถือว่าความลับนั้นถูกเปิดเผยและออกใหม่ การเขียนประวัติใหม่ทำได้ด้วยเครื่องมืออย่าง git filter-repo แต่จะเปลี่ยนแฮชของทุกคอมมิตหลังจากนั้น และต้องประสานงานกับทีม
ผลที่ตามมาอีกอย่าง: เพื่อนร่วมทีมที่ทำ git pull หลังจากไฟล์ถูกนำออกจากดัชนีด้วย --cached จะพบว่าไฟล์ถูกลบออกจากดิสก์ของพวกเขาด้วย สำหรับ .env ให้แจ้งทีมล่วงหน้า หรือเก็บเทมเพลต .env.example ไว้ในรีโพซิทอรี
git rm: การลบไฟล์
git rm old.py # ลบจากดิสก์และจากดัชนี
git rm -r legacy/ # ลบโฟลเดอร์
git rm -n '*.tmp' # dry run: แสดงสิ่งที่จะถูกลบ
git rm -f changed.py # ลบไฟล์ที่มีการแก้ไขที่ยังไม่คอมมิต
หากลบไฟล์ด้วย rm ธรรมดา Git จะเห็นว่าไฟล์ "deleted" และการลบต้องถูกเพิ่มผ่าน git add หรือ git rm git rm ทำสองขั้นตอนนี้พร้อมกัน หากไม่ใส่ -f มันจะปฏิเสธการลบไฟล์ที่มีการแก้ไขที่ยังไม่คอมมิต ซึ่งเป็นการป้องกันการสูญเสียงาน
git mv: การเปลี่ยนชื่อและย้ายไฟล์
git mv utils.py helpers.py
git mv helpers.py src/helpers.py
git mv คือ mv + git rm ของพาธเดิม + git add ของพาธใหม่ Git ไม่เก็บข้อมูลว่ามีการเปลี่ยนชื่อ แต่คำนวณจากความคล้ายของเนื้อหาเมื่อแสดง git status, git log --follow และ git diff ดังนั้นการใช้ git mv และการใช้ mv ตามด้วย git add -A ให้ผลเหมือนกัน
คำแนะนำเชิงปฏิบัติ: เปลี่ยนชื่อและแก้เนื้อหาไฟล์ในคอมมิตที่ต่างกัน หากไฟล์เปลี่ยนไปมากพร้อมกับการเปลี่ยนชื่อ Git อาจไม่รู้จักว่าเป็นไฟล์เดิม และประวัติด้วย --follow จะขาดตอน
สำหรับการเปลี่ยนเฉพาะตัวพิมพ์ใหญ่เล็กของชื่อบน Windows และ macOS (ระบบไฟล์ที่ไม่สนใจตัวพิมพ์) ใช้ git mv readme.md README.md เพราะการเปลี่ยนชื่อธรรมดาใน File Explorer อาจไม่ถูกสังเกตโดย Git
ลำดับกฎและความสำคัญ
ภายในไฟล์เดียวกันกฎอ่านจากบนลงล่าง และกฎที่ตรงกันล่าสุดชนะ ดังนั้นข้อยกเว้น !keep.log ต้องอยู่ต่ำกว่ากฎ *.log มิฉะนั้นจะถูกทับ ระหว่างไฟล์ ลำดับความสำคัญคือ: .gitignore ในโฟลเดอร์ย่อยมีความสำคัญกว่าที่ราก ส่วนการละเว้นส่วนกลางและ .git/info/exclude มีความสำคัญต่ำที่สุด
ตัวอย่าง .gitignore สำหรับเว็บโปรเจกต์
ด้านล่างคือเวอร์ชันที่ใช้งานได้จริงสำหรับโปรเจกต์ Node.js ที่มีแบ็กเอนด์ Python ให้สังเกตคอมเมนต์ ผ่านไปครึ่งปีจะไม่มีใครจำได้ว่าทำไมต้องมีกฎแต่ละข้อ
# --- dependencies ---
node_modules/
.venv/
# --- บิลด์และแคช ---
/dist
/build
__pycache__/
*.pyc
.pytest_cache/
coverage/
# --- ล็อก ---
*.log
npm-debug.log*
# --- สภาพแวดล้อมและความลับ ---
.env
.env.*
!.env.example
# --- ไฟล์ที่ผู้ใช้อัปโหลด แต่เก็บโฟลเดอร์ไว้ ---
uploads/*
!uploads/.gitkeep
ที่นี่ใช้เทคนิคข้อยกเว้นทั้งสองแบบ: .env.* ซ่อนไฟล์สภาพแวดล้อมในเครื่องทั้งหมด แต่เทมเพลต .env.example ยังอยู่ในรีโพซิทอรี และโฟลเดอร์ uploads มีอยู่ในโคลนเพราะมี .gitkeep
สิ่งที่ไม่ควรละเว้น
การละเว้นมากเกินไปก็เป็นอันตราย ไฟล์ล็อกของ dependencies อย่าง package-lock.json, poetry.lock, composer.lock โดยทั่วไปถูกคอมมิตในแอปพลิเคชัน เพราะรับประกันว่านักพัฒนาทุกคนและ CI ติดตั้งแพ็กเกจเวอร์ชันเดียวกัน การตั้งค่าการจัดรูปแบบร่วม (.editorconfig และคอนฟิกของลินเตอร์) ก็ควรอยู่ในรีโพซิทอรี
กรณีที่ถกเถียงกันคือการตั้งค่า IDE โฟลเดอร์ .vscode/ มักถูกละเว้นทั้งหมด แต่บางครั้งคอมมิตคำแนะนำส่วนขยายและงานบิลด์: .vscode/* แล้วตามด้วย !.vscode/extensions.json การตัดสินใจเป็นของทีม สิ่งสำคัญคืออย่าเก็บพาธส่วนตัวและคีย์ในรีโพซิทอรีร่วม
สถานการณ์: คอมมิต node_modules โดยไม่ตั้งใจ
ความผิดพลาดคลาสสิกของมือใหม่: รัน git add . ก่อนสร้าง .gitignore ทำให้ไฟล์หลายหมื่นไฟล์เข้าคอมมิต วิธีแก้มีสามขั้นตอน:
echo "node_modules/" >> .gitignore
git rm -r --cached node_modules
git commit -m "chore: นำ node_modules ออกจากรีโพซิทอรีแล้ว"
หากคอมมิตยังไม่ได้ส่งขึ้นเซิร์ฟเวอร์ ควรเขียนคอมมิตใหม่เพื่อไม่ให้ไฟล์หนัก ๆ ค้างในประวัติ: git reset --soft HEAD~1 แล้วรันคำสั่งชุดเดิมและคอมมิตใหม่ หลังจาก push ไฟล์อยู่ในประวัติแล้ว และขนาดของโคลนจะไม่ลดลงหากไม่เขียนประวัติใหม่
เพื่อไม่ให้เกิดเหตุการณ์แบบนี้ซ้ำ ให้สร้าง .gitignore ก่อน git add ครั้งแรก และตรวจ git status ก่อนคอมมิตทุกครั้ง รายการไฟล์ใหม่ที่ยาวผิดปกติคือสัญญาณให้หยุด
สรุป
ไฟล์ .gitignore คือไฟล์แรกที่ควรคอมมิตในโปรเจกต์ใหม่ ไม่ต้องให้ dependencies บิลด์ ล็อก และความลับเข้ารีโพซิทอรี การละเว้นทำงานเฉพาะกับไฟล์ที่ยังไม่ถูกติดตาม ดังนั้นสิ่งที่ถูกเพิ่มไปแล้วให้นำออกด้วย git rm --cached และตรวจกฎด้วย git check-ignore -v สำหรับการลบและเปลี่ยนชื่อใช้ git rm และ git mv เพื่อให้การเปลี่ยนแปลงเข้าดัชนีทันที การทำงานกับดัชนีและรีโพซิทอรีแรกถูกอธิบายและฝึกจริงในหลักสูตร Git
คำถามที่พบบ่อย
ต้องคอมมิตไฟล์ .gitignore เองหรือไม่?
ใช่ เพื่อให้กฎมีผลกับทั้งทีม ส่วนข้อยกเว้นส่วนตัวใส่ไว้ใน .git/info/exclude หรือการละเว้นส่วนกลาง
มีไฟล์ .gitignore หลายไฟล์ได้หรือไม่?
ได้ วางไว้ในโฟลเดอร์ย่อยใดก็ได้ กฎจะอ้างอิงจากโฟลเดอร์นั้นและเสริมกฎที่ราก
จะเพิ่มไฟล์ที่ถูกละเว้นเข้าคอมมิตได้อย่างไร?
ใช้ git add -f พาธ/ของไฟล์ หลังจากนั้นไฟล์จะถูกติดตาม และ .gitignore จะไม่มีผลกับไฟล์นั้นอีก
จะดูไฟล์ที่ถูกละเว้นทั้งหมดในโปรเจกต์ได้อย่างไร?
รัน git status --ignored หรือ git ls-files --others --ignored --exclude-standard
จะหยุดติดตามการเปลี่ยนแปลงของไฟล์ที่คอมมิตแล้วชั่วคราวได้อย่างไร?
ใช้ git update-index --skip-worktree ไฟล์ แต่เป็นการตั้งค่าในเครื่อง และไม่ใช่ทางเลือกแทน .gitignore
หัวข้อนี้สอนอย่างละเอียดพร้อมฝึกปฏิบัติในคอร์ส “Git с нуля до уверенного разработчика”
ไปที่คอร์ส