.gitignore Cú pháp: bin vs bin / vs. bin / * vs. bin / **


89

Sự khác biệt giữa cách thêm là gì bin, bin/, bin/*bin/**trong tập tin .gitignore của tôi? Tôi đang sử dụng bin/, nhưng nhìn vào các tệp .gitignore khác (trong tệp nhật thực , dấu sao đôi và sao đơn thậm chí còn được sử dụng cùng nhau như thế này: có tmp/**/*chuyện gì vậy?) Tôi thấy rằng hai mẫu đầu tiên cũng được sử dụng rộng rãi. Ai đó có thể vui lòng giải thích sự khác biệt giữa ba?


4
@unutbu: Câu trả lời được chấp nhận cho câu hỏi đó dường như đang bị tranh cãi. Một trong những bình luận hàng đầu khẳng định câu trả lời trên thực tế là một câu chuyện hoang đường.
chandsie

Hành vi được chỉ định hoàn toàn trong trang chủ và tôi chắc chắn rằng có một câu hỏi / câu trả lời xung quanh đây (hoặc mười) bao gồm tất cả thông tin đó.
Cascabel

Câu trả lời:


84

binkhớp với bất kỳ tệp hoặc thư mục nào có tên 'bin'.

bin/khớp với bất kỳ thư mục nào có tên 'bin', có nghĩa là tất cả nội dung của nó vì Git không theo dõi các thư mục một mình.

bin/*khớp với tất cả các tệp và thư mục trực tiếp trong bất kỳ bin/. Điều này ngăn Git tự động tìm bất kỳ tệp nào trong các thư mục con của nó, nhưng nếu, giả sử một bin/foothư mục con được tạo, quy tắc này sẽ không khớp với foonội dung của.

bin/**khớp với tất cả các tệp và thư mục trong bất kỳ bin/thư mục nào và tất cả các thư mục con của nó.

Từ "bất kỳ" là rất quan trọng ở đây vì các quy tắc không liên quan đến gốc kho lưu trữ và áp dụng ở bất kỳ đâu trong cây hệ thống tệp. Bạn phải bắt đầu các quy tắc bằng /(hoặc !/bỏ qua) có nghĩa là gốc của kho lưu trữ, không phải gốc của hệ thống, để chỉ khớp với những gì đã định.

CẢNH BÁO: Bạn không bao giờ được sử dụng các quy tắc như dir/*, /dir/**v.v. một mình trừ khi bạn cũng bỏ qua thứ gì đó tồn tại bên trong thư mục đó . Bỏ qua các dấu sao hoặc bạn vĩnh viễn có thể mất rất nhiều dữ liệu từ lời gọi nhất định git gc, git stashvà nhiều hơn nữa.

Tôi thực sự không biết tmp/**/*phải làm gì. Ban đầu tôi nghĩ rằng nó có thể được sử dụng để khớp các tệp trong thư mục con của tmp/nhưng không phải tệp trực tiếp hiển thị trong tmp/chính nó. Nhưng một thử nghiệm đơn giản dường như cho thấy rằng điều này bỏ qua tất cả các tệp trong tmp/.


10
chỉ để làm rõ, sự khác biệt giữa bin/và là bin/**gì?
chandsie

1
Tôi nghi ngờ bin/sẽ bỏ qua các thư mục bin, trong khi bin/**sẽ bao gồm các thư mục bin nhưng không phải bất kỳ nội dung của nó
Robin Winslow

1
Điều đó có vẻ không phù hợp với câu trả lời của Siddhartha. Rút ra từ câu trả lời, bin/sẽ bỏ qua chính thư mục (bao gồm tất cả các thư mục con và tệp), trong khi bin/**sẽ bỏ qua tất cả các tệp trong thư mục bin và các thư mục con của nó, nhưng không bỏ qua chính thư mục bin. Tôi không chắc điều đó có chính xác hay không.
Christopher Berman

3
lưu ý rằng, nếu bạn muốn theo dõi tất cả các tệp trong thư mục bin / nhưng bỏ qua tất cả các tệp trong thư mục con của nó, bạn có thể làm (ở các dòng tiếp theo) bin/** \n !bin/*(vì tôi không thể thấy cách buộc ngắt dòng trong mini-Markdown)
TomRoche

9
Câu trả lời này sai theo rất nhiều cách. Thứ nhất, git không theo dõi các thư mục, vì vậy một mục nhập .gitignore chỉ có thể khớp với nội dung thư mục, không bao giờ là một thư mục như vậy. Thứ hai, binkhớp với cả tệp có tên bin nội dung của binthư mục. Thứ ba, bin/* không khớp với bất kỳ tệp nào trong thư mục con của nó. Các bạn thậm chí đã kiểm tra điều này?
ThomasR

46

binbin/chỉ khác ở chỗ cái sau sẽ chỉ khớp với một thư mục.

bin/**/*giống như bin/**(dường như kể từ ngày 1.8.2, theo câu trả lời của @ VonC).

Điều khó khăn là tôi chỉ mất một giờ hoặc lâu hơn để vò tóc, là cái đó bin/bin/**không hoàn toàn giống nhau! Vì cái trước bỏ qua toàn bộ thư mục và cái sau bỏ qua từng tệp bên trong nó, và git trong hầu hết các trường hợp không quan tâm đến thư mục, nên thường không có sự khác biệt. Tuy nhiên, nếu bạn cố gắng sử dụng !để bỏ qua một đường dẫn con, thì bạn sẽ thấy rằng git (ahem) bỏ qua nó nếu bạn bỏ qua thư mục mẹ! (một lần nữa, thay vì nội dung thư mục)

Đây là ví dụ rõ ràng nhất, vì vậy đối với một kho lưu trữ init-ed mới được thiết lập như vậy:

$ 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

Các tệp chưa được kiểm tra sau tồn tại:

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

Nhưng bạn có thể thấy các tệp sau không bị bỏ qua:

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

Và nếu bạn cố gắng thêm, bạn sẽ nhận được:

$ 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

Tôi coi hành vi này là một lỗi. (Đây là tất cả trên git version 1.8.4.msysgit.0)


1
+1, thực sự. Bạn nên xem xét gửi một báo cáo lỗi thực sự về điều này vì hành vi này có vẻ không mong muốn.
chandsie

1
Chính xác là trường hợp sử dụng của tôi. Cảm ơn!
Sebastian Graf

3
Các hành vi khác nhau của dir/dir/**lại. bỏ bỏ qua với !xảy ra vì "Không thể bao gồm lại tệp nếu thư mục mẹ của tệp đó bị loại trừ" [nguồn ]. Khó hiểu, nhưng được thực hiện vì lý do hiệu suất. Xem một câu hỏi SO liên quan .
tanius

23

Lưu ý rằng, nói một cách chính xác, git không theo dõi các thư mục, chỉ theo dõi các tệp. Do đó không thể thêm một thư mục, chỉ có nội dung của nó .

.gitignoreTuy nhiên, trong bối cảnh đó , git giả vờ hiểu các thư mục vì lý do duy nhất là

Không thể bao gồm lại tệp nếu thư mục mẹ của tệp đó bị loại trừ.
https://git-scm.com/docs/gitignore#_pattern_format

Điều này có ý nghĩa gì đối với các mẫu loại trừ? Hãy đi qua chúng một cách chi tiết:

bin

Điều này bỏ qua

  • tệp có tên bin.
  • nội dung của các thư mục có tên bin

Bạn có thể đưa vào danh sách trắng bincác tệp và thư mục bị bỏ qua bằng cách thêm các !mục nhập tiếp theo , nhưng bạn không thể đưa nội dung của các thư mục có tên vào danh sách trắngbin

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/

Tương tự như trên, ngoại trừ nó không khớp với các tệp được đặt tên bin. Việc thêm một dấu chỉ /cho git khớp với các thư mục.

bin/*

Điều này bỏ qua

  • tệp chứa trong một thư mục có tênbin
  • nội dung của các thư mục con trực tiếp của các thư mục có tên 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/**

Điều này bỏ qua

  • nội dung của bin
  • nội dung của các thư mục con (bất kỳ mức lồng nào) trong 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

Tôi vừa tạo một repo mới và thử một số thứ. Đây là kết quả của tôi:

KẾT QUẢ MỚI

git phiên bản 2.10.1.windows.1

  1. Khởi tạo repo gần như trống rỗng. Chỉ tệp README
  2. Điền vào binthư mục sâu nhiều lớp
    • 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. Thêm binvào gitignore: Kết quả
    • Mọi thứ bên dưới binthư mục (và sâu hơn) hiện bị bỏ qua
    • Mức gốc không bị bỏ qua (/bin.txt và /Test.txt vẫn hiển thị)
  4. Chỉnh sửa binđể bin/ở gitignore: Kết quả
    • Không thay đổi
  5. Chỉnh sửa bin/thànhbin/*
    • Không thay đổi
  6. Chỉnh sửa bin/*thànhbin/**
    • Không thay đổi
  7. Chỉnh sửa bin/**thànhbin/**/
    • bin/bin.txtbin/Test.txtkhông còn bị bỏ qua
  8. Chỉnh sửa bin/**/thànhbin/**/*
    • bin/bin.txtbin/Test.txttrở lại bị bỏ qua

KẾT QUẢ CŨ

phiên bản git: 2.7.0.windows.1

  1. Khởi tạo repo gần như trống rỗng. Chỉ tệp README
  2. Điền vào binthư mục sâu nhiều lớp
    • bin/a/b/Test.txt
    • bin/a/bin/Test.txt
    • bin/a/Test.txt
    • bin/Test.txt
  3. Thêm binvào gitignore: Kết quả
    • Mọi thứ bên dưới binthư mục (và sâu hơn) hiện bị bỏ qua
  4. Chỉnh sửa binđể bin/ở gitignore: Kết quả
    • Mọi thứ bên dưới binthư mục (và sâu hơn) vẫn bị bỏ qua (không thay đổi)
  5. Chỉnh sửa bin/thànhbin/*
    • Mọi thứ bên dưới binthư mục (và sâu hơn) vẫn bị bỏ qua (không thay đổi)
  6. Chỉnh sửa bin/*thànhbin/**
    • Mọi thứ bên dưới binthư mục (và sâu hơn) vẫn bị bỏ qua (không thay đổi)
  7. Chỉnh sửa bin/**thànhbin/**/
    • bin/Test.txt không còn bị bỏ qua
  8. Chỉnh sửa bin/**/thànhbin/**/*
    • Mọi thứ bên dưới binthư mục (và sâu hơn) lại bị bỏ qua

1
Đây là một thử nghiệm tốt, tôi nghĩ rằng đổi tên text.txt thành bin.txt sẽ tốt hơn cho thấy một số khác biệt chính. Nếu bạn đã làm điều này, tôi sẽ bỏ phiếu cho câu trả lời này.
Matt Johnson

@MattJohnson Đúng ... tiếc là tôi trên một phiên bản mới hơn git vào lúc này
Joe Phillips

@MattJohnson Cuối cùng tôi cũng hiểu được điều này
Joe Phillips

8

Lưu ý rằng ' **', khi được kết hợp với thư mục con ( **/bar), phải thay đổi so với hành vi mặc định của nó, vì ghi chú phát hành cho git1.8.2 hiện đề cập:

Các mẫu trong .gitignore.gitattributestệp có thể có **/, như một mẫu phù hợp với 0 hoặc nhiều cấp độ của thư mục con.

Ví dụ: " foo/**/bar" khớp " bar" trong foochính "" hoặc trong thư mục con của " foo".


Quy tắc cần nhớ (và giúp hiểu sự khác biệt của mục đích đằng sau những cú pháp đó) là:

Không thể bao gồm lại tệp nếu thư mục mẹ của tệp đó bị loại trừ.


Thông thường, nếu bạn muốn loại trừ tệp khỏi thư mục con của thư mục bỏ qua f, bạn sẽ làm như sau:

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

Đó là:

  • Nếu quy tắc đầu tiên là f/, thư mục f/sẽ bị bỏ qua và các quy tắc dưới đây liên quan đến fsẽ không quan trọng.
  • f/**đạt được giống như f/, nhưng bỏ qua tất cả các phần tử con (tệp và thư mục con).
    Cung cấp cho bạn cơ hội để danh sách trắng (loại trừ từ gitignore) các thư mục con: !f/**/.
  • Vì tất cả các fthư mục con không bị bỏ qua, bạn có thể thêm quy tắc để loại trừ tệp ( !f/a/sub/folder/someFile.txt)

Làm thế nào để trả lời câu hỏi này?
ThomasR

0

Có một sự khác biệt khác giữa bin/*bin/.

bin/phù hợp foo/bin/test.txt(như mong đợi), nhưng bin/*không, có vẻ lạ, nhưng nó được ghi lại: https://git-scm.com/docs/gitignore

"Documentation / *. Html" khớp với "Documentation / git.html" nhưng không khớp với "Documentation / ppc / ppc.html" hoặc "tools / perf / Documentation / perf.html".

Lý do cho điều này dường như là các quy tắc sau:

  • Nếu mẫu kết thúc bằng một dấu gạch chéo, nó sẽ bị xóa vì mục đích mô tả sau…

  • Nếu mẫu không chứa dấu gạch chéo /, Git coi nó như một mẫu hình cầu bao và kiểm tra xem có khớp với tên đường dẫn liên quan đến vị trí của tệp .gitignore…

  • Nếu không, Git coi mẫu như một khối cầu phù hợp để tiêu thụ bởi fnmatch (3) với cờ FNM_PATHNAME…

Vì vậy, nếu mẫu kết thúc bằng một dấu gạch chéo, thì dấu gạch chéo sẽ bị xóa và nó được coi là một mẫu hình cầu vỏ, trong trường hợp đó sẽ binkhớp foo/bin/test.txt. Nếu nó kết thúc bằng /*, dấu gạch chéo sẽ không bị xóa và nó được chuyển đến fnmatch, không khớp trong thư mục con.

Tuy nhiên, điều này cũng không đúng đối với foo/bin/foo/bin/*, bởi vì ngay cả sau khi loại bỏ dấu gạch chéo foo/bin/, nó vẫn chứa một dấu gạch chéo, vì vậy nó được coi là một mẫu fnmatch chứ không phải một hình cầu. Tức là nó sẽ không khớpbar/foo/bin/test.txt

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.