11 คะแนน โดย GN⁺ 4 시간 전 | 3 ความคิดเห็น | แชร์ทาง WhatsApp
  • กฎการละเว้นไฟล์ของ 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 ความคิดเห็น

 
sudoeng 1 시간 전

ไฟล์อย่างสเปกงานหรือ plan.md ก็น่าจะมีประโยชน์ถ้าใส่ไว้ใน .git/info/exclude

 
yangeok 2 시간 전

อ๋อ มีแบบตั้งค่าที่ root ได้ด้วยสินะ 555

 
GN⁺ 4 시간 전
ความคิดเห็นจาก 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 ได้เสมอนั้นอันตราย
    • ในฐานะคนที่ทำทั้งการอัปเกรด dependency และไล่หาสาเหตุของบั๊ก ผมคงหงุดหงิดมากถ้า git diff ไม่แสดง diff ของ lockfile
      เข้าใจได้ว่ามันดูเหมือน noise ระดับบรรทัด แต่เวลาที่ต้องใช้ มันจำเป็นจริง ๆ
  • การตั้งค่า exclude แบบ global/ระดับผู้ใช้เป็นฟีเจอร์ที่ควรถูกพูดถึงให้มากกว่านี้ ผมได้รับการเปลี่ยนแปลงที่พยายามเพิ่มไฟล์จาก IDE/OS/AI ลงใน .gitignore ของทุกโปรเจกต์บ่อยมาก แต่ถ้าบอกคนอื่นว่าใส่ไว้ในค่ามาตรฐานได้เลย มันจะถูก ignore ทุกที่โดยไม่ต้องแตะแต่ละโปรเจกต์ และยังลดความเสี่ยงที่จะเผลอ commit จากโปรเจกต์ที่ยังไม่ได้อัปเดต .gitignore ด้วย ส่วนใหญ่ก็ชอบกัน
    หลักส่วนตัวของผมคือ .gitignore ใน repository ควรใช้กับ รายการที่เป็นของเฉพาะ repository เท่านั้น เช่น build artifact หรือโฟลเดอร์ dependency ส่วนเครื่องมือของผู้ใช้ก็ควรอยู่ในค่าตั้งส่วนตัวของแต่ละคน

    • การที่ต้องคอยบอกเรื่อง global .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
    • ผมก็ทำแบบนี้เหมือนกัน แค่ใช้ชื่อ .local
    • ของผมคือ scratch/
      ตอนนี้ยังไม่เคยมีปัญหา
  • เรื่อง ignore รายผู้ใช้ ถ้าเป็น macOS ก็ว่ากันว่าควรใส่ .DS_Store ไว้ตรงนั้น แต่ผู้ใช้ Mac ทุกคนในโปรเจกต์ก็ต้องทำแบบนั้น
    ถ้ามีเกินหนึ่งคน ก็อาจจะดีกว่าถ้าไม่ปล่อยให้แต่ละคนจัดการเอง

    • ผมไม่แน่ใจว่ามันมาจากไหน แต่ Mac สองเครื่องของผม (เครื่องหนึ่ง Ventura อีกเครื่อง Sequoia) มี .DS_Store อยู่ในไฟล์ ~/.gitignore_global ทั้งคู่ และในการตั้งค่า Git แบบ global ก็มีการตั้งให้ ignore รายการจากไฟล์นั้นด้วย
      วันที่ของไฟล์บน Mac เครื่องใหม่คือสองวันก่อนสั่งซื้อ และผมจำไม่ได้ว่าเคยตั้งเอง เลยเดาว่ามันน่าจะติดมาจากค่าเริ่มต้น Mac เครื่องเก่าก็น่าจะคล้ายกัน และเมื่อดูจากเวอร์ชัน macOS ก็เป็นไปได้ว่าค่านี้อาจเป็นค่าเริ่มต้นมานานพอสมควรแล้ว
      งั้นยุคที่ต้องคอยเพิ่ม .DS_Store/ ลงใน .gitignore อาจจะจบไปแล้วก็ได้
    • วิธีมองเรื่องคนส่วนน้อยกับคนส่วนมากของคุณค่อนข้างแปลกนะ ถ้ามีผู้ใช้ macOS คนหนึ่งทำงานกับสิบโปรเจกต์ เขาควรต้องเพิ่มบรรทัดนั้นในทั้งสิบโปรเจกต์ หรือควรให้จัดการจากฝั่งผู้ใช้คนนั้นเองจะดีกว่า?
  • โอ้โห ทำไมผมไม่เคยรู้เรื่องนี้มาก่อนนะ? ผมเป็นนักพัฒนาซอฟต์แวร์มืออาชีพมา 20 ปีแล้ว แต่ใช้แค่ .gitignore มาตลอด
    เพิ่งตระหนักว่าตัวเองไม่เคยถามเลยว่ามีวิธีที่ดีกว่านี้ไหม แทนที่จะทำให้ .gitignore รกรุงรังไปด้วยรายการ exclude ที่เกี่ยวข้องแค่กับผม
    ผมก็แค่ยอมรับโลกที่เห็นตรงหน้าไปเฉย ๆ
    วันนี้โลกดีขึ้นอีกนิดแล้ว

  • ผมใช้ .git/info/exclude บ่อยมาก มันเหมาะมากกับสคริปต์/Makefile ที่ใช้แค่ในเครื่องตัวเองและไม่จำเป็นหรือใช้งานไม่ได้สำหรับผู้ร่วมงาน

    • อยากรู้ว่ามีตัวอย่างสคริปต์ที่ผู้ร่วมงานคนอื่นใช้ไม่ได้แบบไหนบ้าง เช่นสคริปต์สำหรับ workflow การทำ PR อะไรทำนองนั้นหรือเปล่า?
    • ผมใช้ shell function มานานพอสมควรแล้ว ที่เอาไฟล์ untracked ทั้งหมดที่โผล่ใน 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-unchanged
    unassume = update-index --no-assume-unchanged
    assumed = "!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 ได้