- กฎการละเว้นไฟล์ของ Git แบ่งออกเป็น 3 ระดับ ตามขอบเขตการใช้งานร่วมกัน ได้แก่
.gitignore, .git/info/exclude, และ ~/.config/git/ignore
- เนื่องจาก
.gitignore ถูกคอมมิตไปพร้อมกับโค้ดในรีโพซิทอรี จึงเป็นที่สำหรับเก็บ กฎที่ใช้ร่วมกัน ซึ่งทีมหรือโปรเจ็กต์ควรใช้ด้วยกัน
- รายการอย่างไฟล์ส่วนตัวหรือไฟล์สำหรับงานในเครื่อง ซึ่งจำเป็นต่อรีโพซิทอรีแต่ไม่ค่อยเหมาะจะทำเป็นกฎของทีม ควรใส่ไว้ใน
.git/info/exclude
- ไฟล์ที่ต้องการตัดออกซ้ำ ๆ จากทุกรีโพซิทอรี เช่น
.DS_Store บน macOS สามารถใส่ไว้ใน ~/.config/git/ignore ซึ่งเป็น ไฟล์ ignore ระดับเครื่อง ได้
git check-ignore -v <ชื่อไฟล์> มีประโยชน์เมื่ออยากตามดูว่ากฎใดเป็นตัวละเว้นไฟล์นั้น และหากไม่มีกฎที่ตรงกันก็จะไม่มีเอาต์พุต
ตำแหน่งที่ใช้กฎ ignore ของ Git
- Git สามารถประมวลผลกฎการละเว้นไฟล์ได้จาก 3 ตำแหน่ง
.gitignore
.git/info/exclude
~/.config/git/ignore
.gitignore: กฎที่ใช้ร่วมกันและถูกคอมมิตเข้ารีโพซิทอรี
.gitignore คือไฟล์ทั่วไปที่ใช้เขียนชื่อไฟล์ที่ต้องการละเว้น
- ไฟล์นี้จะถูก เช็กอินเข้า Git ไปพร้อมกับโค้ดส่วนอื่น
- ไฟล์ที่ตรงกับกฎใน
.gitignore จะไม่ถูกนำมาพิจารณาเมื่อรันคำสั่ง git
.git/info/exclude: กฎส่วนตัวรายรีโพซิทอรี
- ไฟล์
exclude อยู่ภายในไดเรกทอรี .git ของทุกรีโพซิทอรี Git
- การเปลี่ยนแปลงในไฟล์นี้จะ ไม่ถูกเช็กอินเข้า Git
- ในรีโพซิทอรี Git ใหม่ ไฟล์นี้มักจะมีคอมเมนต์อยู่ไม่กี่บรรทัด
- เหมาะสำหรับไฟล์ที่อยากละเว้นเฉพาะในรีโพซิทอรีนั้น แต่ไม่อยากใส่ไว้ใน
.gitignore
- ตัวอย่าง: ถ้าต้องการไม่คอมมิต
notes.txt ที่ใช้เฉพาะในเวิร์กโฟลว์ส่วนตัว และก็ไม่อยากเพิ่มลงใน .gitignore ของโปรเจ็กต์ ให้เพิ่ม notes.txt ลงใน .git/info/exclude
~/.config/git/ignore: กฎระดับเครื่องแบบ global
- ไฟล์
ignore แบบ global อยู่ที่ ~/.config/git/ignore ในโฮมไดเรกทอรี
- ชื่อไฟล์ที่เพิ่มไว้ที่นี่จะถูก ละเว้นแบบ global ในระดับเครื่อง
- ไฟล์นี้จะไม่ถูกเช็กอินเข้า Git และไม่ได้ผูกกับรีโพซิทอรีใดโดยเฉพาะ
- จึงเป็นตำแหน่งที่เหมาะสำหรับใส่ไฟล์ที่อยากละเว้นในทุกรีโพซิทอรี Git บนคอมพิวเตอร์เครื่องนั้น
- ตัวอย่าง: บน macOS การเพิ่ม
.DS_Store ไว้ที่นี่ถือว่าเหมาะสม
เปลี่ยนพาธของไฟล์ global ignore
- สามารถกำหนดให้ไฟล์ global ignore ไปใช้ไฟล์อื่นได้
- หากต้องการใช้
.gitignore_global เป็นไฟล์ Git ignore แบบ global ให้รันคำสั่งต่อไปนี้
git config --global core.excludesFile ~/.gitignore_global
- หากต้องการกลับไปใช้ค่าปริยาย ให้รันคำสั่งต่อไปนี้
git config --global --unset core.excludesFile
ตรวจสอบว่ากฎใดกำลังละเว้นไฟล์
- ใช้
git check-ignore -v <ชื่อไฟล์> เพื่อตรวจสอบว่าไฟล์ใดไฟล์หนึ่งถูกละเว้นด้วยกฎข้อใด
- หากต้องการตรวจสอบว่า
.DS_Store ถูกละเว้นอย่างไร ให้รันคำสั่งต่อไปนี้ภายในรีโพซิทอรี Git
git check-ignore -v .DS_Store
- หาก
.gitignore ของรีโพซิทอรีเป็นตัวละเว้น .DS_Store ตัวอย่างเอาต์พุตจะเป็นดังนี้
$ git check-ignore -v .DS_Store
.gitignore:1:.DS_Store .DS_Store
- หาก
.git/info/exclude ของรีโพซิทอรีเป็นตัวละเว้น .DS_Store ตัวอย่างเอาต์พุตจะเป็นดังนี้
$ git check-ignore -v .DS_Store
.git/info/exclude:7:.DS_Store .DS_Store
- หากไฟล์ global
~/.config/git/ignore เป็นตัวละเว้น .DS_Store ตัวอย่างเอาต์พุตจะเป็นดังนี้
$ git check-ignore -v .DS_Store
/Users/nelson/.config/git/ignore:2:.DS_Store .DS_Store
- หากไฟล์ global ignore แบบกำหนดเอง
.gitignore_global เป็นตัวละเว้น .DS_Store ตัวอย่างเอาต์พุตจะเป็นดังนี้
$ git check-ignore -v .DS_Store
/Users/nelson/.gitignore_global:1:.DS_Store .DS_Store
- หากไม่มีกฎใดที่ละเว้นไฟล์ที่ระบุ คำสั่ง
git check-ignore -v จะ ไม่แสดงเอาต์พุตใด ๆ
3 ความคิดเห็น
ไฟล์อย่างสเปกงานหรือ
plan.mdก็น่าจะมีประโยชน์ถ้าใส่ไว้ใน.git/info/excludeอ๋อ มีแบบตั้งค่าที่ root ได้ด้วยสินะ 555
ความคิดเห็นจาก Hacker News
บทความน่าสนใจ แต่ขาดฟีเจอร์ เกือบจะ ignore ที่ผมชอบที่สุดใน Git ไป นั่นคือ
.gitattributesไฟล์นี้ใช้กำหนดให้ Git "ละเลย" ความต่างของไฟล์บางไฟล์ได้ ตัวอย่างเช่น
package-lock.jsonในโปรเจกต์ Node นั้น จากมุมมองของ Git มันแทบจะเป็นแค่สัญญาณรบกวนล้วน ๆ เพราะมีแต่ diff ขนาดใหญ่ที่เต็มไปด้วยเวอร์ชันเฉพาะของไลบรารี ขณะที่ข้อมูลเวอร์ชันจริงที่มนุษย์อ่านง่ายอยู่ในpackage.jsonแยกต่างหากถ้าเพิ่มบรรทัด
package-lock.json -diffลงใน.gitattributesที่รากโปรเจกต์ ไฟล์นี้ก็ยังคงถูก stage/commit ตามปกติ แต่ในgit diffจะไม่เห็นความต่างก้อนใหญ่ที่ไม่มีความหมายpackage-lock.jsonไม่ควรถูกมองว่าเป็นสัญญาณรบกวน ถ้าไม่ได้ตั้งใจจะอัปเดตมันก็ไม่ควรเปลี่ยนมัน ไม่อย่างนั้นก็จะเปิดรับ ความเสี่ยงด้านซัพพลายเชน โดยไม่มีเหตุผลถ้า
package-lock.jsonมีการเปลี่ยนแปลงแบบไม่คาดคิดบ่อย ๆ แปลว่าคุณกำลังทำอะไรผิดอยู่package-lock.jsonแสดง dependency แบบถ่ายทอดต่อ ทั้งหมด ส่วนpackage.jsonแสดงเฉพาะ direct dependency เท่านั้น ดังนั้นการบอกว่าอย่างหลังคือ "เวอร์ชันจริงที่มนุษย์อ่านได้" จึงไม่ถูกต้องทั้งสองไฟล์มีคนละจุดประสงค์ และการบอกว่าสามารถมองข้าม diff ของ lockfile ได้เสมอนั้นอันตราย
git diffไม่แสดง diff ของ lockfileเข้าใจได้ว่ามันดูเหมือน noise ระดับบรรทัด แต่เวลาที่ต้องใช้ มันจำเป็นจริง ๆ
การตั้งค่า exclude แบบ global/ระดับผู้ใช้เป็นฟีเจอร์ที่ควรถูกพูดถึงให้มากกว่านี้ ผมได้รับการเปลี่ยนแปลงที่พยายามเพิ่มไฟล์จาก IDE/OS/AI ลงใน
.gitignoreของทุกโปรเจกต์บ่อยมาก แต่ถ้าบอกคนอื่นว่าใส่ไว้ในค่ามาตรฐานได้เลย มันจะถูก ignore ทุกที่โดยไม่ต้องแตะแต่ละโปรเจกต์ และยังลดความเสี่ยงที่จะเผลอ commit จากโปรเจกต์ที่ยังไม่ได้อัปเดต.gitignoreด้วย ส่วนใหญ่ก็ชอบกันหลักส่วนตัวของผมคือ
.gitignoreใน repository ควรใช้กับ รายการที่เป็นของเฉพาะ repository เท่านั้น เช่น build artifact หรือโฟลเดอร์ dependency ส่วนเครื่องมือของผู้ใช้ก็ควรอยู่ในค่าตั้งส่วนตัวของแต่ละคน.gitignoreอยู่เรื่อย ๆ เป็นผลลัพธ์ตามธรรมชาติของหลักการที่ว่า.gitignoreใน repository ควรใช้กับของเฉพาะ repository เท่านั้นถ้าอยากประหยัดเวลาของทุกคน ก็ใส่ไฟล์พวกนั้นไว้ใน
.gitignoreของทุกโปรเจกต์ไปเลยน่าจะดีกว่า.gitignoreของโปรเจกต์มาตลอด เพื่อกันไม่ให้คนที่ไม่รู้เผลอเพิ่มไฟล์พวกนั้นเข้าไปในโปรเจกต์สุดท้ายก็ต้องลบมันออกจาก Git อยู่ดี และคนคนนั้นก็จะเจอความยุ่งยาก ผมเลยกันไว้ก่อนด้วยความหวังดี อนาคตอาจจะใจดีน้อยลงหน่อยก็ได้
gitignoreเพราะมันยังอยู่แม้จะ rebuild development containerถ้าจะเลี่ยง
gitignoreก็ยังพอใช้สคริปต์สร้างหรือ volume เพื่อกู้คืน/คงค่าตั้งไว้ได้ แต่ก็ต้องมีสคริปต์เพิ่มหรือการตั้งค่า mount ของdevcontainerแทนที่จะจบด้วย.gitignoreแค่บรรทัดเดียวสำหรับการตั้งค่า Git แบบ global และไฟล์ ignore ผมว่าควรใช้
~/.config/git/ignoreกับ~/.config/git/configมากกว่าการสร้าง~/.gitignore_globalแล้วไปแก้ค่า configถ้าใช้
~/.config/สำหรับหลายอย่าง ระดับรากของ dotfiles จะเล็กลงมากเหตุผลที่ Git exclude ถูกใช้น้อยกว่าคือมันไม่ได้ถูก commit เข้า repository เลยต้องสร้างใหม่ทุกครั้งที่อยากใช้ ไม่ได้แปลว่ามันไม่ดี แค่นั่นคือเหตุผลที่มันถูกใช้น้อย
~/.configก็จะช่วยให้แก้ไขภายหลังและแชร์ต่อได้~/.cvsignoreสำหรับเครื่องมืออื่นที่ใช้ไฟล์แบบเดียวกันก็ได้ไม่รู้ไปเรียนมาจากไหน แต่ผมเพิ่ม
atticไว้ใน global Git ignoreแบบนี้ก็สร้างไดเรกทอรี
atticไว้เก็บของจุกจิกที่ไม่ควร commit เด็ดขาดได้ในทุกโปรเจกต์ เท่าที่จำได้ยังไม่เคยเจอ repository ไหนที่ตรวจไดเรกทอรีชื่อนี้จริงจังถ้ามีไดเรกทอรีอย่าง
atticก็สร้างattic/.gitignoreข้างในแล้วใส่/**ลงไป แบบนี้ทั้งไดเรกทอรีและทุกอย่างข้างในจะถูก ignore รวมถึงไฟล์ ignore เองด้วยปกติผมตั้งชื่อไดเรกทอรีเวอร์ชันของตัวเองเป็นอักขระเดียว U+1F4A9 แต่ HN ไม่ยอมให้ใส่มันในคอมเมนต์
auxแล้วใส่
.gitignoreที่มีแค่ดอกจัน*ตัวเดียวไว้ข้างในเพื่อซ่อนมัน เท่านี้ตัวมันเองกับทุกอย่างข้างในก็จะถูก ignore.localscratch/ตอนนี้ยังไม่เคยมีปัญหา
เรื่อง ignore รายผู้ใช้ ถ้าเป็น macOS ก็ว่ากันว่าควรใส่
.DS_Storeไว้ตรงนั้น แต่ผู้ใช้ Mac ทุกคนในโปรเจกต์ก็ต้องทำแบบนั้นถ้ามีเกินหนึ่งคน ก็อาจจะดีกว่าถ้าไม่ปล่อยให้แต่ละคนจัดการเอง
.DS_Storeอยู่ในไฟล์~/.gitignore_globalทั้งคู่ และในการตั้งค่า Git แบบ global ก็มีการตั้งให้ ignore รายการจากไฟล์นั้นด้วยวันที่ของไฟล์บน Mac เครื่องใหม่คือสองวันก่อนสั่งซื้อ และผมจำไม่ได้ว่าเคยตั้งเอง เลยเดาว่ามันน่าจะติดมาจากค่าเริ่มต้น Mac เครื่องเก่าก็น่าจะคล้ายกัน และเมื่อดูจากเวอร์ชัน macOS ก็เป็นไปได้ว่าค่านี้อาจเป็นค่าเริ่มต้นมานานพอสมควรแล้ว
งั้นยุคที่ต้องคอยเพิ่ม
.DS_Store/ลงใน.gitignoreอาจจะจบไปแล้วก็ได้โอ้โห ทำไมผมไม่เคยรู้เรื่องนี้มาก่อนนะ? ผมเป็นนักพัฒนาซอฟต์แวร์มืออาชีพมา 20 ปีแล้ว แต่ใช้แค่
.gitignoreมาตลอดเพิ่งตระหนักว่าตัวเองไม่เคยถามเลยว่ามีวิธีที่ดีกว่านี้ไหม แทนที่จะทำให้
.gitignoreรกรุงรังไปด้วยรายการ exclude ที่เกี่ยวข้องแค่กับผมผมก็แค่ยอมรับโลกที่เห็นตรงหน้าไปเฉย ๆ
วันนี้โลกดีขึ้นอีกนิดแล้ว
ผมใช้
.git/info/excludeบ่อยมาก มันเหมาะมากกับสคริปต์/Makefileที่ใช้แค่ในเครื่องตัวเองและไม่จำเป็นหรือใช้งานไม่ได้สำหรับผู้ร่วมงานgit statusโยนเข้า.git/info/excludeปกติจะใช้หลังจาก
addและcommitสิ่งที่อยากเก็บไว้ใน repository แล้วผมใช้ไฟล์ excludes แบบนี้เพื่อแยกใช้ การตั้งค่า Git ระดับโปรเจกต์ ที่ต่างกันในไดเรกทอรีโปรเจกต์ที่มีหลาย repository
https://laszlo.nu/blog/project-level-git-config.html
ผมมี alias ที่ใช้เกี่ยวกับเรื่องนี้อยู่
assume = update-index --assume-unchangedunassume = update-index --no-assume-unchangedassumed = "!git ls-files -v | grep ^h | cut -c 3-"unassumeall = "!git assumed | xargs git update-index --no-assume-unchanged"assumeall = "!git st -s | awk {'print $2'} | xargs git assume"สำหรับไฟล์ที่ถูก track อยู่แล้ว ยังมี
git update-index --[no]-skip-worktreeด้วยมันอาจมีประโยชน์กับการทดลองในเครื่อง แต่ Git ไม่ได้ทำให้มันมองเห็นได้ง่ายนัก เลยใช้งานค่อนข้างเกะกะ ต้องจำให้ได้ว่าตั้งค่าอะไรไว้ และถ้าลืม มันอาจไปขัดงานอื่นอย่างการ checkout ได้