.gitignore Syntax: bin vs bin / กับ bin / * เทียบกับ bin / **


90

ความแตกต่างระหว่างการเพิ่มbin, bin/, bin/*และbin/**ในแฟ้ม .gitignore ของฉันได้อย่างไร ฉันใช้bin/แต่ดูไฟล์. gitignore อื่น ๆ (ในไฟล์ eclipseดาวคู่และดาวเดียวยังใช้ร่วมกันเช่นนี้เกิดอะไรขึ้น?) ฉันเห็นว่าสองรูปแบบแรกก็ใช้กันอย่างแพร่หลายเช่นกัน ใครช่วยอธิบายความแตกต่างระหว่างทั้งสามได้ไหมtmp/**/*


4
@unutbu: เห็นได้ชัดว่าคำตอบที่ยอมรับสำหรับคำถามนั้นมีข้อโต้แย้ง หนึ่งในความคิดเห็นยอดนิยมอ้างว่าคำตอบนี้เป็นตำนานที่สมบูรณ์
chandsie

พฤติกรรมถูกระบุไว้อย่างสมบูรณ์ใน manpage และฉันแน่ใจว่ามีคำถาม / คำตอบอยู่ที่นี่ (หรือสิบข้อ) ที่มีข้อมูลทั้งหมดนั้น
Cascabel

3
ด้วยความเคารพ**: stackoverflow.com/questions/1470572/…
Cascabel

คำตอบ:


85

binตรงกับไฟล์หรือไดเร็กทอรีที่ชื่อว่า 'bin'

bin/ตรงกับไดเรกทอรีใด ๆ ที่ชื่อ 'bin' ซึ่งหมายความว่าเนื้อหาทั้งหมดเนื่องจาก Git ไม่ได้ติดตามไดเรกทอรีเพียงอย่างเดียว

bin/*จับคู่ไฟล์และไดเรกทอรีทั้งหมดโดยตรงในไฟล์bin/. ซึ่งจะป้องกันไม่ให้ Git ค้นหาไฟล์ใด ๆ ในไดเร็กทอรีย่อยโดยอัตโนมัติ แต่หากกล่าวว่ามีการสร้างbin/fooไดเร็กทอรีย่อยกฎนี้จะไม่ตรงกับfooเนื้อหาของ

bin/**จับคู่ไฟล์และไดเร็กทอรีทั้งหมดในbin/ไดเร็กทอรีใด ๆและไดเร็กทอรีย่อยทั้งหมด

คำว่า "any" มีความสำคัญในที่นี้เนื่องจากกฎไม่สัมพันธ์กับรูทที่เก็บและใช้ที่ใดก็ได้ในโครงสร้างระบบไฟล์ คุณต้องเริ่มต้นกฎด้วย a /(หรือ!/ยกเลิกการละเว้น) ซึ่งหมายถึงรูทของที่เก็บไม่ใช่รูทของระบบเพื่อให้ตรงกับสิ่งที่ตั้งใจไว้เท่านั้น

คำเตือน:คุณควรไม่เคยใช้กฎเช่นdir/*, /dir/**ฯลฯ เพียงอย่างเดียวนอกจากคุณจะยกเลิกไม่สนใจสิ่งที่อยู่ภายในไดเรกทอรีที่ ละเว้นเครื่องหมายดอกจันหรือคุณอาจสูญเสียอย่างถาวรข้อมูลจำนวนมากจากบางสวดของgit gc, git stashและอื่น ๆ

ฉันไม่รู้จริงๆว่าtmp/**/*ตั้งใจจะทำอะไร ตอนแรกฉันคิดว่ามันสามารถใช้เพื่อจับคู่ไฟล์ในไดเร็กทอรีย่อยของtmp/แต่ไม่ใช่ไฟล์ที่มีอยู่ในtmp/ตัวมันเองโดยตรง แต่การทดสอบง่ายๆดูเหมือนจะแนะนำว่าสิ่งนี้จะละเว้นไฟล์ทั้งหมดในtmp/.


10
เพียงแค่ชี้แจงความแตกต่างระหว่างbin/และbin/**อะไร?
chandsie

1
ฉันสงสัยว่าbin/จะไม่สนใจไดเร็กทอรี bin ในขณะที่bin/**จะรวมไดเร็กทอรี bin แต่จะไม่รวมเนื้อหาใด ๆ
Robin Winslow

1
ดูเหมือนจะไม่สอดคล้องกับคำตอบของสิทธัตถะ เมื่อวาดจากคำตอบbin/จะละเว้นไดเร็กทอรีเอง (รวมถึงไดเร็กทอรีย่อยและไฟล์ทั้งหมด) ในขณะที่bin/**จะละเว้นไฟล์ทั้งหมดในไดเร็กทอรี bin และไดเร็กทอรีย่อย แต่ไม่ใช่ไดเร็กทอรี bin เอง ไม่ว่าจะถูกต้องหรือไม่ฉันไม่แน่ใจ
Christopher Berman

3
โปรดทราบว่าหากคุณต้องการติดตามไฟล์ทั้งหมดในไดเร็กทอรี bin / แต่ละเว้นไฟล์ทั้งหมดในไดเร็กทอรีย่อยคุณสามารถทำได้ (ในบรรทัดต่อมา) bin/** \n !bin/*(เนื่องจากฉันไม่เห็นวิธีบังคับให้ linebreak ใน mini-Markdown)
TomRoche

9
คำตอบนี้ผิดในหลาย ๆ ด้าน ขั้นแรก git จะไม่ติดตามไดเร็กทอรีดังนั้นรายการ. gitignore สามารถจับคู่เนื้อหาไดเร็กทอรีเท่านั้นไม่เคยเป็นไดเร็กทอรีดังกล่าว ประการที่สองbinจับคู่ทั้งไฟล์ที่มีชื่อbin และเนื้อหาของbinโฟลเดอร์ ประการที่สามbin/* ไม่ตรงกับไฟล์ใด ๆ ในไดเรกทอรีย่อย พวกคุณได้ทดสอบสิ่งนี้หรือไม่?
ThomasR

46

binและbin/แตกต่างกันเพียงว่าหลังจะตรงกับไดเรกทอรีเท่านั้น

bin/**/*เหมือนกับbin/**(เห็นได้ชัดตั้งแต่ 1.8.2 ตามคำตอบของ @ VonC)

สิ่งที่ยุ่งยากที่ฉันใช้เวลาเพียงหนึ่งชั่วโมงหรือมากกว่านั้นในการรวบผมของฉันออกไปนั้นคือbin/และbin/**ไม่เหมือนกันเลยทีเดียว ! เนื่องจากก่อนหน้านี้ละเว้นไดเร็กทอรีโดยรวมและไฟล์หลังละเว้นแต่ละไฟล์ที่อยู่ภายในและคอมไพล์ในเกือบทุกกรณีไม่สนใจไดเร็กทอรีจึงไม่มีความแตกต่างกัน อย่างไรก็ตามหากคุณพยายามใช้!เพื่อยกเลิกการเพิกเฉยต่อพา ธ ย่อยคุณจะพบว่า git (อะแฮ่ม) เพิกเฉยหากคุณไม่สนใจไดเรกทอรีหลัก! (อีกครั้งแทนที่จะเป็นเนื้อหาไดเรกทอรี)

ตัวอย่างนี้ชัดเจนที่สุดดังนั้นสำหรับที่เก็บ init-ed ที่เพิ่งตั้งค่าไว้:

$ cat .gitignore
ignored-file
or-dir
dir-only/
!dir-only/cant-reinclude
dir-contents/**
!dir-contents/can-reinclude

$ mkdir or-dir dir-only dir-contents

$ touch file ignored-file or-dir/ignored-file dir-only/cant-reinclude dir-contents/can-reinclude

มีไฟล์ที่ไม่ได้ติดตามต่อไปนี้:

$ git ls-files --other
.gitignore
dir-contents/can-reinclude
dir-only/cant-reinclude
file
ignored-file
or-dir/ignored-file

แต่คุณจะเห็นว่าไฟล์ต่อไปนี้ไม่ถูกละเลย:

$ git ls-files --other --exclude-standard
.gitignore
dir-contents/can-reinclude
file

และถ้าคุณพยายามเพิ่มคุณจะได้รับ:

$ git add dir-only/cant-reinclude
The following paths are ignored by one of your .gitignore files:
dir-only/cant-reinclude
Use -f if you really want to add them.
fatal: no files added

ฉันถือว่าพฤติกรรมนี้เป็นข้อบกพร่อง (ทั้งหมดนี้เปิดอยู่git version 1.8.4.msysgit.0)


1
+1 แน่นอน คุณควรพิจารณายื่นรายงานข้อบกพร่องที่เกิดขึ้นจริงเกี่ยวกับเรื่องนี้เนื่องจากพฤติกรรมดูเหมือนจะไม่คาดคิด
chandsie

1
ตรงกับกรณีการใช้งานของฉัน ขอบคุณ!
Sebastian Graf

3
พฤติกรรมที่แตกต่างกันของdir/และdir/**อีกครั้ง ยกเลิกการละเว้นด้วย!เกิดขึ้นเนื่องจาก "ไม่สามารถรวมไฟล์อีกครั้งได้หากไม่รวมไดเร็กทอรีหลักของไฟล์นั้น" [source ] สับสน แต่ทำด้วยเหตุผลด้านประสิทธิภาพ ดูคำถาม SO เกี่ยวข้อง
tanius

23

โปรดทราบว่าพูดอย่างเคร่งครัด git ไม่ได้ติดตามไดเรกทอรี แต่ไฟล์เท่านั้น ด้วยเหตุนี้จึงไม่สามารถเพิ่มไดเร็กทอรีได้มีเพียงเนื้อหาเท่านั้น

อย่างไรก็ตามในบริบทของ.gitignoregit แสร้งทำเป็นเข้าใจไดเรกทอรีด้วยเหตุผลเดียวว่า

ไม่สามารถรวมไฟล์อีกครั้งได้หากไม่รวมไดเร็กทอรีหลักของไฟล์นั้น
https://git-scm.com/docs/gitignore#_pattern_format

นี่หมายความว่าอย่างไรสำหรับรูปแบบการยกเว้น มาดูรายละเอียดกัน:

bin

สิ่งนี้ละเว้น

  • ไฟล์ชื่อbin.
  • เนื้อหาของโฟลเดอร์ที่ชื่อ bin

คุณสามารถกำหนดbinไฟล์และโฟลเดอร์ที่ถูกละเว้นได้โดยการเพิ่ม!รายการที่ตามมาแต่คุณไม่สามารถอนุญาตพิเศษเนื้อหาของโฟลเดอร์ที่ชื่อbin

bin

!bin/file_in_bin # has no effect, since bin/ is blacklisted!
!bin/* # has no effect, since bin/ is blacklisted!
!file_in_bin # has no effect, since bin/ is blacklisted!

!bin # this works

bin/

เหมือนข้างบน แต่มันไม่ตรงกับไฟล์binชื่อ การเพิ่มการต่อท้าย/จะบอกให้คอมไพล์ตรงกับไดเร็กทอรีเท่านั้น

bin/*

สิ่งนี้ละเว้น

  • ไฟล์ที่อยู่ในโฟลเดอร์ชื่อbin
  • เนื้อหาของโฟลเดอร์ย่อยโดยตรงของโฟลเดอร์ที่ชื่อ bin
bin/*  # blacklists bin/file_in_bin and bin/subfolder/

!bin/subfolder/file_in_sub # has no effect, since bin/subfolder is blacklisted!
!bin # whitelists files named bin/bin, since bin/ itself is not blacklisted
!bin/ # has no effect, since bin/ itself is not blacklisted


!bin/file_in_bin # works since bin/ itself is not blacklisted
!file_in_bin # works too
!bin/subfolder # works (so implicitly whitelists bin/subfolder/file_in_sub)
!bin/subfolder/ # works just as well
!bin/* # works for file_in_bin and subfolder/

bin/**

สิ่งนี้ละเว้น

  • เนื้อหาของ bin
  • เนื้อหาของโฟลเดอร์ย่อย (ระดับการซ้อนกัน) ภายใน bin
bin/**  # blacklists bin/file_in_bin and
        # bin/subfolder/ and bin/subfolder/file_in_sub and
        # bin/subfolder/2/ and bin/subfolder/2/file_in_sub_2

!bin/subfolder/file_in_sub # has no effect, since bin/subfolder is blacklisted
!bin/subfolder/2/ # has no effect, since bin/subfolder is blacklisted
!bin/subfolder/2/file_in_sub_2 # has no effect, since bin/subfolder is blacklisted

!bin/subfolder # works only in combinations with other whitelist entries,
               # since all contents of subfolder are blacklisted (1)

!bin/file_in_bin # works since bin itself is not blacklisted
!bin/* # works for file_in_bin and subfolder; see (1)

9

ฉันเพิ่งสร้าง repo ใหม่และลองทำบางอย่าง นี่คือผลลัพธ์ของฉัน:

ผลลัพธ์ใหม่

git เวอร์ชัน 2.10.1.windows.1

  1. เริ่มต้น repo ที่เกือบว่างเปล่า เฉพาะไฟล์ README
  2. เติมข้อมูลในbinไดเร็กทอรีลึกหลาย ๆ ชั้น
    • bin.txt
    • Test.txt
    • bin/a/b/bin.txt
    • bin/a/b/Test.txt
    • bin/a/bin/bin.txt
    • bin/a/bin/Test.txt
    • bin/a/bin.txt
    • bin/a/Test.txt
    • bin/bin.txt
    • bin/Test.txt
  3. เพิ่มbinใน gitignore: ผลลัพธ์
    • ตอนนี้ทุกอย่างที่อยู่ใต้binไดเร็กทอรี (และลึกกว่า) จะถูกละเว้น
    • ระดับรากจะไม่ถูกละเว้น (/bin.txt และ /Test.txt ยังคงแสดงอยู่)
  4. แก้ไขbinไปbin/ใน gitignore นี้: ผลการค้นหา
    • ไม่มีการเปลี่ยนแปลง
  5. แก้ไขbin/เป็นbin/*
    • ไม่มีการเปลี่ยนแปลง
  6. แก้ไขbin/*เป็นbin/**
    • ไม่มีการเปลี่ยนแปลง
  7. แก้ไขbin/**เป็นbin/**/
    • bin/bin.txtและbin/Test.txtไม่ถูกละเลยอีกต่อไป
  8. แก้ไขbin/**/เป็นbin/**/*
    • bin/bin.txtและbin/Test.txtกลับถูกเพิกเฉย

ผลลัพธ์เก่า ๆ

เวอร์ชัน git: 2.7.0.windows.1

  1. เริ่มต้น repo ที่เกือบว่างเปล่า เฉพาะไฟล์ README
  2. เติมข้อมูลในbinไดเร็กทอรีลึกหลาย ๆ ชั้น
    • bin/a/b/Test.txt
    • bin/a/bin/Test.txt
    • bin/a/Test.txt
    • bin/Test.txt
  3. เพิ่มbinใน gitignore: ผลลัพธ์
    • ตอนนี้ทุกอย่างที่อยู่ใต้binไดเร็กทอรี (และลึกกว่า) จะถูกละเว้น
  4. แก้ไขbinไปbin/ใน gitignore นี้: ผลการค้นหา
    • ทุกสิ่งที่อยู่ใต้binไดเร็กทอรี (และลึกกว่า) จะยังคงถูกละเว้น (ไม่มีการเปลี่ยนแปลง)
  5. แก้ไขbin/เป็นbin/*
    • ทุกสิ่งที่อยู่ใต้binไดเร็กทอรี (และลึกกว่า) จะยังคงถูกละเว้น (ไม่มีการเปลี่ยนแปลง)
  6. แก้ไขbin/*เป็นbin/**
    • ทุกสิ่งที่อยู่ใต้binไดเร็กทอรี (และลึกกว่า) จะยังคงถูกละเว้น (ไม่มีการเปลี่ยนแปลง)
  7. แก้ไขbin/**เป็นbin/**/
    • bin/Test.txt ไม่ถูกละเลยอีกต่อไป
  8. แก้ไขbin/**/เป็นbin/**/*
    • ทุกอย่างที่อยู่ใต้binไดเร็กทอรี (และลึกกว่า) จะถูกละเว้นอีกครั้ง

1
นี่เป็นการทดสอบที่ดีฉันคิดว่าการเปลี่ยนชื่อ text.txt เป็น bin.txt จะแสดงความแตกต่างที่สำคัญได้ดีกว่า ถ้าคุณทำเช่นนี้ฉันจะโหวตสำหรับคำตอบนี้
Matt Johnson

@MattJohnson True ... น่าเสียดายที่ฉันกำลังใช้ git เวอร์ชันใหม่ในขณะนี้
Joe Phillips

@MattJohnson ในที่สุดฉันก็มาถึงเรื่องนี้
Joe Phillips

8

โปรดสังเกตว่า ' **' เมื่อรวมกับไดเร็กทอรีย่อย ( **/bar) จะต้องเปลี่ยนจากพฤติกรรมเริ่มต้นเนื่องจากตอนนี้บันทึกประจำรุ่นสำหรับ git1.8.2กล่าวถึง:

รูปแบบใน.gitignoreและ.gitattributesไฟล์สามารถมีได้**/เป็นรูปแบบที่ตรงกับระดับไดเร็กทอรีย่อย 0 หรือมากกว่า

เช่น " foo/**/bar" จับคู่ " bar" ใน " foo" เองหรือในไดเรกทอรีย่อยของ " foo"


กฎที่ต้องจำ (และช่วยให้เข้าใจความแตกต่างของเจตนาที่อยู่เบื้องหลังไวยากรณ์เหล่านั้น) คือ:

ไม่สามารถรวมไฟล์อีกครั้งได้หากไม่รวมไดเร็กทอรีหลักของไฟล์นั้น


โดยทั่วไปหากคุณต้องการแยกไฟล์ออกจากโฟลเดอร์ย่อยของโฟลเดอร์ที่ละเว้น f คุณจะต้องทำดังนี้

f/**
!f/**/
!f/a/sub/folder/someFile.txt

นั่นคือ:

  • หากกฎข้อแรกคือf/โฟลเดอร์f/จะถูกละเว้นและกฎด้านล่างเกี่ยวกับfจะไม่สำคัญ
  • f/**ประสบความสำเร็จเช่นเดียวf/แต่ไม่สนใจย่อยองค์ประกอบทั้งหมด (ไฟล์และโฟลเดอร์ย่อย)
    ที่ช่วยให้คุณมีโอกาสที่จะรายการที่อนุญาต (ไม่รวมจาก gitignore) !f/**/โฟลเดอร์ย่อย:
  • เนื่องจากไม่มีการละเว้นfโฟลเดอร์ย่อยทั้งหมดคุณสามารถเพิ่มกฎเพื่อยกเว้นไฟล์ ( )!f/a/sub/folder/someFile.txt

สิ่งนี้ตอบคำถามได้อย่างไร?
ThomasR

0

มีความแตกต่างของผู้อื่นระหว่างและbin/*bin/

bin/ตรงกันfoo/bin/test.txt(ตามที่คาดไว้) แต่bin/*ไม่ได้ซึ่งดูแปลก แต่มีการบันทึกไว้: https://git-scm.com/docs/gitignore

"Documentation / *. html" ตรงกับ "Documentation / git.html" แต่ไม่ใช่ "Documentation / ppc / ppc.html" หรือ "tools / perf / Documentation / perf.html"

เหตุผลนี้ดูเหมือนจะเป็นกฎเหล่านี้:

  • หากรูปแบบลงท้ายด้วยเครื่องหมายทับจะถูกลบออกเพื่อวัตถุประสงค์ของคำอธิบายต่อไปนี้ ...

  • หากรูปแบบไม่มีเครื่องหมายทับ / Git จะถือว่าเป็นรูปแบบเชลล์ลูกโลกและตรวจสอบการจับคู่กับชื่อพา ธ ที่สัมพันธ์กับตำแหน่งของไฟล์. gitignore ...

  • มิฉะนั้น Git จะถือว่ารูปแบบเป็นเชลล์โกลที่เหมาะสำหรับการบริโภคโดย fnmatch (3) ด้วยแฟล็ก FNM_PATHNAME ...

ดังนั้นถ้ารูปแบบปลายด้วยการเฉือนเฉือนจะถูกลบออกและก็ถือว่าเป็นรูปแบบเปลือก glob ซึ่งในกรณีนี้การแข่งขันbin foo/bin/test.txtหากลงท้ายด้วย/*เครื่องหมายทับจะไม่ถูกลบออกและจะถูกส่งไปยัง fnmatch ซึ่งไม่ตรงกับไดเรกทอรีย่อย

อย่างไรก็ตามสิ่งเดียวกันนี้ไม่เป็นความจริงสำหรับfoo/bin/และfoo/bin/*เนื่องจากแม้หลังจากลบเครื่องหมายทับออกfoo/bin/แล้วก็ยังมีเครื่องหมายทับอยู่ดังนั้นจึงถือว่าเป็นรูปแบบ fnmatch ไม่ใช่ glob คือมันจะไม่ตรงกันbar/foo/bin/test.txt

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.