.gitignore loại trừ thư mục nhưng bao gồm thư mục con cụ thể


964

Tôi có thư mục application/mà tôi thêm vào .gitignore. Bên trong application/thư mục là thư mục application/language/gr. Làm thế nào tôi có thể bao gồm thư mục này?

Tôi đã thử điều này

application/
!application/language/gr/

không có may mắn ...


1
Hy vọng rằng .gitignoretài liệu "định dạng mẫu" đã rõ ràng hơn (tháng 12 năm 2013). Xem câu trả lời của tôi dưới đây
VonC

Câu hỏi và câu trả lời yêu thích của tôi, được thêm vào mục yêu thích cũng như đánh dấu trình duyệt.
codekiddy

Câu trả lời:


1613

Nếu bạn loại trừ application/, thì mọi thứ bên dưới sẽ luôn bị loại trừ (ngay cả khi một số mẫu loại trừ tiêu cực sau này (có thể không phù hợp) có thể khớp với thứ gì đó bên dưới application/).

Để làm những gì bạn muốn, bạn phải chuyển sang unignore, mỗi thư mục cha mẹ của bất cứ thứ gì bạn muốn để xóa unignore. Thông thường, bạn kết thúc việc viết các quy tắc cho tình huống này theo cặp: bỏ qua mọi thứ trong một thư mục, nhưng không phải là một thư mục con nào đó.

# you can skip this first one if it is not already excluded by prior patterns
!application/

application/*
!application/language/

application/language/*
!application/language/gr/

Lưu ý
Các dấu vết /*là đáng kể:

  • Mẫu dir/không bao gồm một thư mục có tên dirvà (ngầm) mọi thứ bên dưới nó.
    Với dir/, Git sẽ không bao giờ nhìn vào bất cứ thứ gì bên dưới dir, và do đó sẽ không bao giờ áp dụng bất kỳ mẫu nào trong số các mẫu không loại trừ vào bất cứ thứ gì bên dưới dir.
  • Các mô hình dir/*không nói gì về dirchính nó; nó chỉ loại trừ mọi thứ bên dưới dir. Với dir/*, Git sẽ xử lý các nội dung trực tiếp của dir, tạo cơ hội cho các mẫu khác có thể loại bỏ một số nội dung ( !dir/sub/).

12
Là dấu hoa thị có ý nghĩa? Nếu vậy, sự khác biệt trong ý nghĩa là gì? Theo thuật toán được mô tả trong tài liệu gitignore , kết thúc bằng dấu gạch chéo khớp với một thư mục và các đường dẫn bên dưới thư mục đó. Kết thúc bằng một dấu sao sau đó sẽ được coi là một mô hình toàn cầu. Thử nghiệm cho thấy biến thể dấu hoa thị hoạt động, nhưng không phải là kết thúc chỉ trong một dấu gạch chéo. Tôi muốn hiểu tại sao lại như vậy.
seh

123
@seh: Vâng, dấu vết /*là đáng kể. Nếu một thư mục bị loại trừ, Git sẽ không bao giờ nhìn vào nội dung của thư mục đó. Mẫu dir/không bao gồm một thư mục có tên dirvà (ngầm) mọi thứ bên dưới nó. Các mô hình dir/*không nói gì về dirchính nó; nó chỉ loại trừ mọi thứ bên dưới dir. Với dir/, Git sẽ không bao giờ nhìn vào bất cứ thứ gì bên dưới dir, và do đó sẽ không bao giờ áp dụng bất kỳ mẫu nào trong số các mẫu không loại trừ vào bất cứ thứ gì bên dưới dir. Với dir/*, Git sẽ xử lý các nội dung trực tiếp của dir, tạo cơ hội cho các mẫu khác có thể loại bỏ một số nội dung ( !dir/sub/).
Chris Johnsen

7
Ah, điều đó giải thích nó. Cho dù tôi đã đọc tài liệu gitignore bao nhiêu lần , tôi không bao giờ hiểu khi các mẫu hoàn nguyên không hoạt động. Với lời giải thích của bạn, bây giờ đã rõ ràng. Các gitignore tài liệu cần có một "công thức" để giải thích làm thế nào để làm điều này.
seh

8
Tôi hoàn toàn không thể làm việc này (tập tin crazy .gitignore!), Vì vậy thay vào đó tôi chỉ buộc thêm các tập tin sau khi cd vào thư mục tôi muốn. git add -f .
K0D4

2
Lưu ý rằng bạn không thể dựa vào đầu ra của git status, điều này sẽ cho bạn biết rằng thư mục cấp cao nhất sẽ được thêm vào. Thay vào đó, hãy thực hiện một git addthư mục cấp cao nhất và sau đó git status(hy vọng) sẽ liệt kê tập hợp con của các tệp đã được khớp với mẫu.
Matthew Strawbridge

136

Cam kết 59856de từ Karsten Blees (kblees) cho Git 1.9 / 2.0 (Q1 2014) làm rõ trường hợp đó:

gitignore.txt: làm rõ bản chất đệ quy của các thư mục bị loại trừ

Một tiền tố tùy chọn " !" phủ định mẫu; bất kỳ tệp phù hợp nào được loại trừ bởi một mẫu trước đó sẽ được đưa vào lại.

Không thể bao gồm lại một tệp nếu thư mục mẹ của tệp đó bị loại trừ. ( *)
( *: trừ khi một số điều kiện được đáp ứng trong git 2.8+, xem bên dưới)
Git không liệt kê các thư mục bị loại trừ vì lý do hiệu suất, do đó, bất kỳ mẫu nào trên các tệp được chứa đều không có hiệu lực, bất kể chúng được xác định ở đâu.

Đặt dấu gạch chéo ngược (" \") trước "" đầu tiên !cho các mẫu bắt đầu bằng chữ " !", ví dụ: " \!important!.txt".

Ví dụ để loại trừ mọi thứ trừ một thư mục cụ thể foo/bar(lưu ý /*- không có dấu gạch chéo, ký tự đại diện cũng sẽ loại trừ mọi thứ bên trong foo/bar):

 --------------------------------------------------------------
     $ cat .gitignore
     # exclude everything except directory foo/bar
     /*
     !/foo
     /foo/*
     !/foo/bar
 --------------------------------------------------------------

Trong trường hợp của bạn:

application/*
!application/**/
application/language/*
!application/language/**/
!application/language/gr/**

Trước tiên, bạn phải liệt kê các thư mục danh sách trắng trước khi có thể liệt kê các tệp trong một thư mục nhất định.


Cập nhật tháng 2 / 3/2016:

Lưu ý rằng với git 2.9.x / 2.10 (giữa năm 2016?), Có thể bao gồm lại một tệp nếu thư mục mẹ của tệp đó bị loại trừ nếu không có ký tự đại diện trong đường dẫn được bao gồm lại .

Nguyễn Thái Ngọc Duy ( pclouds) đang cố gắng thêm tính năng này:

Vì vậy, với git 2.9+, điều này có thể thực sự hoạt động, nhưng cuối cùng đã được hoàn nguyên:

application/
!application/language/gr/

Tôi đã cố gắng sử dụng cú pháp bao gồm lại được cập nhật được đăng ở cuối câu trả lời của bạn trên git cho windows v2.8.1.windows.1nhưng nó không hoạt động :(
David Hancock

1
@DavidHancock Xin lỗi, tôi đã chỉnh sửa câu trả lời: điều này chưa có sẵn.
VonC

1
@DavidHancock tôi cũng vậy: đó là hơn 13 câu trả lời Stack Overflow mà tôi đã phải chỉnh sửa nhiều lần!
VonC

5
Git 2.9 đã được phát hành ngày hôm qua. Xác nhận mẫu application/+ !application/language/gr/được đề cập trong câu trả lời đang hoạt động như mong đợi.
Ray Shan

1
@RayShan Lạ: Tôi đã thấy cam kết hoàn nguyên hủy bỏ tính năng đó, nhưng tôi chưa thấy (và ghi chú phát hành github.com/git/git/blob/master/Documentation/RelNotes/2.9.0.txt không đề cập) bất kỳ cam kết cải thiện các quy tắc .gitignore.
VonC

52

Câu trả lời của @Chris Johnsen rất hay, nhưng với phiên bản Git mới hơn (1.8.2 trở lên), có một mẫu dấu sao đôi bạn có thể tận dụng cho giải pháp tốc ký hơn một chút:

# assuming the root folder you want to ignore is 'application'
application/**/*

# the subfolder(s) you want to track:
!application/language/gr/

Bằng cách này, bạn không phải "bỏ dấu" thư mục mẹ của thư mục con mà bạn muốn theo dõi.


Với Git 2.17.0 (Không chắc là sớm như thế nào trước phiên bản này. Có thể quay lại 1.8.2), sử dụng **mẫu kết hợp với loại trừ cho mỗi thư mục con dẫn đến (các) tệp của bạn hoạt động. Ví dụ:

# assuming the root folder you want to ignore is 'application'
application/**

# Explicitly track certain content nested in the 'application' folder:
!application/language/
!application/language/gr/
!application/language/gr/** # Example adding all files & folder in the 'gr' folder
!application/language/gr/SomeFile.txt # Example adding specific file in the 'gr' folder

9
Than ôi, điều này không thành công (Git 1.8.4.msysgit.0) vì mẫu **có thể khớp với các thư mục con bằng 0, và *sẽ khớp languagevà loại trừ nó, ngăn chặn việc đưa vào gr. Chuỗi phụ huynh đầy đủ @Chris Johnson khuyến nghị dường như vẫn cần thiết.
Sean Gugler

3
Nghe có vẻ hoàn hảo nhưng nó không hoạt động với tôi trên git 2.3.7 ... /www/**/* !/www/config.xml !/www/resconfig.xml và thư mục res vẫn bị bỏ qua.
Cướp

@Rob bạn cũng cần thêm !/www/res/. Bạn có thể sử dụng folder/**/*mẫu, nhưng bạn vẫn cần thêm loại trừ cho mỗi thư mục con bạn muốn thêm. Nó vẫn ngắn hơn và dễ đọc hơn kết hợp bỏ qua / loại trừ.
Ben Kane

Tôi biết đó là một nhận xét cũ, nhưng trong trường hợp bạn tò mò. Tôi đã thêm một chỉnh sửa để trả lời tài liệu về phương pháp này.
Ben Kane

21

Có một loạt các câu hỏi tương tự về điều này, vì vậy tôi sẽ đăng những gì tôi đã viết trước đây:

Cách duy nhất tôi có được điều này để làm việc trên máy của mình là làm theo cách này:

# Ignore all directories, and all sub-directories, and it's contents:
*/*

#Now ignore all files in the current directory 
#(This fails to ignore files without a ".", for example 
#'file.txt' works, but 
#'file' doesn't):
*.*

#Only Include these specific directories and subdirectories:
!wordpress/
!wordpress/*/
!wordpress/*/wp-content/
!wordpress/*/wp-content/themes/
!wordpress/*/wp-content/themes/*
!wordpress/*/wp-content/themes/*/*
!wordpress/*/wp-content/themes/*/*/*
!wordpress/*/wp-content/themes/*/*/*/*
!wordpress/*/wp-content/themes/*/*/*/*/*

Lưu ý cách bạn phải cho phép rõ ràng nội dung cho từng cấp độ bạn muốn đưa vào. Vì vậy, nếu tôi có 5 thư mục con sâu theo chủ đề, tôi vẫn cần đánh vần nó ra.

Đây là từ nhận xét của @ Yarin tại đây: https://stackoverflow.com/a/5250314/1696153

Đây là những chủ đề hữu ích:

Tôi cũng đã thử

*
*/*
**/**

**/wp-content/themes/**

hoặc là /wp-content/themes/**/*

Không ai trong số đó làm việc cho tôi cả. Rất nhiều thử nghiệm và sai sót!


1
Lưu ý: Hãy nhớ rằng thứ tự cũng quan trọng trong tệp .gitignore, vì vậy hãy đảm bảo bạn đặt !quy tắc của mình ở dưới cùng.
starbeamrainbowlabs

15

Tôi chỉ tìm thấy điều này thực sự hoạt động.

**/node_modules/*
!**/node_modules/keep-dir

tuyệt vời, hãy cứu lấy ngày của tôi)
Yegor Zaremba

cũng đã cứu tôi
tomrlh

8

Cách đơn giản nhất và có lẽ là tốt nhất là thử thêm các tệp theo cách thủ công (nói chung, việc này được ưu tiên hơn .gitignorecác quy tắc theo kiểu):

git add /path/to/module

Bạn thậm chí có thể muốn có -N ý định thêm cờ, để đề nghị bạn sẽ thêm chúng, nhưng không phải ngay lập tức. Tôi thường làm điều này cho các tập tin mới Tôi chưa sẵn sàng để trình diễn.


Đây là bản sao của một câu trả lời được đăng trên những gì có thể dễ dàng là một QA trùng lặp. Tôi đang đăng lại nó ở đây để tăng khả năng hiển thị. Tôi thấy dễ dàng hơn khi không có sự lộn xộn của các quy tắc gitignore.


2
Cảm ơn câu trả lời này. Tôi đã phải thêm -f vì nó nằm trong .gitignore của tôi rất giống vậy: git add -f path / to / file
jasonflaherty

6

Vì vậy, vì nhiều lập trình viên sử dụng nút. trường hợp sử dụng đáp ứng câu hỏi này là để loại trừnode_modules trừ ngoại trừ một mô-đun module-achẳng hạn:

!node_modules/

node_modules/*
!node_modules/module-a/

2
Không hoạt động cho phiên bản git 2.10.2.windows.1.
Sebastian

1
Đối với phiên bản git 2.10.2, hãy làm theo câu trả lời này
nalexn

👌 tuyệt vời 👋 👋.
Abdennour TOUMI

6

Thêm một câu trả lời bổ sung:

!/.vs/              <== include this folder to source control, folder only, nothing else
/.vs/*              <== but ignore all files and sub-folder inside this folder
!/.vs/ProjectSettings.json <== but include this file to source control
!/.vs/config/       <== then include this folder to source control, folder only, nothing else
!/.vs/config/*      <== then include all files inside the folder

đây là kết quả:

nhập mô tả hình ảnh ở đây


2
Đây là câu trả lời hữu ích nhất đối với tôi khi hình ảnh giúp ích. Cảm ơn!
Joel Murphy

4

Đặc biệt đối với các phiên bản Git cũ hơn, hầu hết các đề xuất sẽ không hoạt động tốt. Nếu đó là trường hợp, tôi sẽ đặt một .gitignore riêng trong thư mục nơi tôi muốn bao gồm nội dung bất kể các cài đặt khác và cho phép có những gì cần thiết.

Ví dụ: /.gitignore

# ignore all .dll files
*.dll

/dependency_files/.gitignore

# include everything
!*

Vì vậy, mọi thứ trong / Depency_files (thậm chí các tệp dll) đều được bao gồm tốt.


4

Tôi đã tìm thấy một trường hợp tương tự ở đây, trong đó trong laravel theo mặc định, .gitignorebỏ qua tất cả bằng cách sử dụng dấu hoa thị, sau đó ghi đè lên thư mục công cộng.

*
!public
!.gitignore

Điều này là không đủ nếu bạn chạy vào kịch bản OP.

Nếu bạn muốn cam kết một thư mục con cụ thể của public, ví dụ như trong public/productsthư mục của bạn, bạn muốn bao gồm các tệp nằm sâu một thư mục con, ví dụ như bao gồm public/products/a/b.jpgchúng sẽ không được phát hiện chính xác, ngay cả khi bạn thêm chúng cụ thể như thế này !/public/products,!public/products/* , vv ..

Giải pháp là đảm bảo bạn thêm một mục nhập cho mọi cấp độ đường dẫn như thế này để ghi đè tất cả chúng.

*
!.gitignore
!public/
!public/*/
!public/products/
!public/products/*
!public/products/*/
!public/products/*/
!public/products/*/*

3

Chỉ là một ví dụ khác về việc đi xuống cấu trúc thư mục để có được chính xác những gì bạn muốn. Lưu ý: Tôi không loại trừ Library/nhưngLibrary/**/*

# .gitignore file
Library/**/*
!Library/Application Support/
!Library/Application Support/Sublime Text 3/
!Library/Application Support/Sublime Text 3/Packages/
!Library/Application Support/Sublime Text 3/Packages/User/
!Library/Application Support/Sublime Text 3/Packages/User/*macro
!Library/Application Support/Sublime Text 3/Packages/User/*snippet
!Library/Application Support/Sublime Text 3/Packages/User/*settings
!Library/Application Support/Sublime Text 3/Packages/User/*keymap
!Library/Application Support/Sublime Text 3/Packages/User/*theme
!Library/Application Support/Sublime Text 3/Packages/User/**/
!Library/Application Support/Sublime Text 3/Packages/User/**/*macro
!Library/Application Support/Sublime Text 3/Packages/User/**/*snippet
!Library/Application Support/Sublime Text 3/Packages/User/**/*settings
!Library/Application Support/Sublime Text 3/Packages/User/**/*keymap
!Library/Application Support/Sublime Text 3/Packages/User/**/*theme

> git add Library

> git status

On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   Library/Application Support/Sublime Text 3/Packages/User/Default (OSX).sublime-keymap
    new file:   Library/Application Support/Sublime Text 3/Packages/User/ElixirSublime.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/Package Control.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/Preferences.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/RESTer.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/SublimeLinter/Monokai (SL).tmTheme
    new file:   Library/Application Support/Sublime Text 3/Packages/User/TextPastryHistory.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/ZenTabs.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/adrian-comment.sublime-macro
    new file:   Library/Application Support/Sublime Text 3/Packages/User/json-pretty-generate.sublime-snippet
    new file:   Library/Application Support/Sublime Text 3/Packages/User/raise-exception.sublime-snippet
    new file:   Library/Application Support/Sublime Text 3/Packages/User/trailing_spaces.sublime-settings

Chính xác những gì tôi muốn làm quá ;-)
Kitze

3

Trong WordPress, điều này đã giúp tôi:

wp-admin/
wp-includes/
/wp-content/*
!wp-content/plugins/
/wp-content/plugins/*
!/wp-content/plugins/plugin-name/
!/wp-content/plugins/plugin-name/*.*
!/wp-content/plugins/plugin-name/**

3

gitignore - Chỉ định các tập tin không theo dõi cố ý bỏ qua.

Ví dụ để loại trừ mọi thứ trừ một thư mục cụ thể foo / bar (lưu ý / * - không có dấu gạch chéo, ký tự đại diện cũng sẽ loại trừ mọi thứ trong foo / bar ):

$ cat .gitignore
# exclude everything except directory foo/bar
/*
!/foo
/foo/*
!/foo/bar

Một ví dụ khác cho WordPress :

!/wp-content
wp-content/*
!/wp-content/plugins
wp-content/plugins/*
!wp-content/plugins/my-awesome-plugin

Thêm thông tin tại đây: https://git-scm.com/docs/gitignore


2

Tôi thường sử dụng cách giải quyết này trong CLI thay vì định cấu hình .gitignore, tôi tạo một .includetệp riêng trong đó tôi xác định các thư mục (phụ) tôi muốn đưa vào mặc dù các thư mục trực tiếp hoặc bị bỏ qua đệ quy.gitignore .

Vì vậy, tôi cũng sử dụng

git add `cat .include`

trong quá trình dàn dựng, trước khi cam kết.

Đối với OP, tôi đề nghị sử dụng một .includedòng có các dòng sau:

<parent_folder_path>/application/language/gr/*

LƯU Ý: Việc sử dụng catkhông cho phép sử dụng các bí danh (bên trong .include) để chỉ định $ HOME (hoặc bất kỳ thư mục cụ thể nào khác). Điều này là do dòng homedir/app1/* khi được chuyển sang git addsử dụng lệnh trên xuất hiện dưới dạng git add 'homedir/app1/*'và bao quanh các ký tự trong dấu ngoặc đơn ('') giữ giá trị bằng chữ của mỗi ký tự trong dấu ngoặc kép, do đó ngăn các bí danh (như homedir ) hoạt động (xem Bash Single Báo giá ).

Đây là một ví dụ về một .includetập tin tôi sử dụng trong repo của tôi ở đây .

/home/abhirup/token.txt
/home/abhirup/.include
/home/abhirup/.vim/*
/home/abhirup/.viminfo
/home/abhirup/.bashrc
/home/abhirup/.vimrc
/home/abhirup/.condarc

1

Tôi muốn theo dõi các tập tin js sản xuất jquery và điều này đã làm việc:

node_modules/*
!node_modules/jquery
node_modules/jquery/*
!node_modules/jquery/dist/*

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.