Технически, что происходит, так это то, что обработчику атрибутов присваивается имя файла и список правил из всех различных файлов .gitattributes
(их может быть больше одного). Для каждого имени файла:
Имя подразумевает некоторый каталог, например, когда имя scripts/foo
, 1 , файл находится в каталоге scripts
, поэтому файл атрибутов из этого каталога (scripts/.gitattributes
) имеет приоритет, а .gitattributes
из каталога src/
(src/.gitattributes
) вообще не применяется.
Начиная с файл с самым низким приоритетом , Git применяет каждую строку, чтобы проверить, совпадает ли она. Если соответствует , применяются все атрибуты в этой строке. Если более ранняя строка устанавливает атрибут, эта более поздняя строка переопределяет его. В противном случае ранее установленный атрибут все еще применяется.
Git повторяет этот процесс для всех применимых файлов атрибутов. Так как те, которые имеют более высокий приоритет, применяются , позже , их строки переопределяются. Последние строки таких файлов переопределяют более ранние строки.
В этом случае у вас было две применимые строки в одном файле .gitattributes
, которые будут применяться к scripts/test1.bat
. Таким образом, для этого файла:
*.bat text eol=crlf
scripts/* text eol=lf
первая строка соответствует и устанавливает eol=crlf
, затем более поздняя строка соответствует и устанавливает text
и устанавливает eol=lf
.
В результате получается, что окончательная группировка атрибутов для этого конкретного файла: text
(набор), eol=lf
.
Вы не пробовали это в качестве примера, но предположим, что у вас есть файл с именем scripts/a.exe
. Есть строка:
*.exe binary
вместе со строкой:
scripts/* text eol=lf
, поэтому комбинация здесь выглядит как binary
(установлено), text
(установлено) и eol=lf
. Однако binary
сам по себе является макросом . Вы можете определить свои собственные макросы (хотя только в некоторых файлах атрибутов). В этом случае макрос расширяется до -diff -merge -text
.
Каждый -
означает unset . Таким образом, наряду с отменой сброса diff
и merge
, первая соответствующая строка выполняла операцию unset text
. Вторая строка соответствия, scripts/*
, выполнила операцию set text
, которая отменила предыдущую операцию unset text
. Конечным результатом является то, что для этого файла установлено text
, diff
не установлено, merge
не установлено и eol
установлено в lf
.
Обратите внимание, что unset отличается от не указано . Атрибут unspecified foo
означает, что ни в одной строке явно не было -foo
или foo
или foo=value
. 2 За исключением предопределенных макросов и имен атрибутов, вызванных в документации имена атрибутов на самом деле не зафиксированы: в будущих версиях Git могут быть добавлены новые, и вы можете устанавливать или отменять или задавать имена атрибутов, которые вы создаете сами, и Git ничего не скажу о них. Запись:
* foo=bar
не имеет никакого эффекта, потому что ничего внутри Git в настоящее время не ищет атрибута foo
, но если будущее Git решит использовать foo
для обозначения чего-либо, оно может внезапно начаться делать вещи.
Довольно сложно справиться со всем этим, поэтому существует git check-attr
. См. phd's answer .
1 Git превращает все обратные косые черты в внутренние. Возможно, вы захотите отдать предпочтение форварду sla sh, потому что, например, \b
означает backspace для некоторых команд оболочки; \t
означает tab , \r
означает возврат каретки и так далее. Если вы используете sh или bash, оболочка будет делать с ними свое дело, поэтому зарезервируйте их для оболочки. Не используйте их в именах путей. Используйте косую черту - в любом случае проще набирать!
2 Если в какой-то предыдущей строке был задан, не задан или задан некоторый атрибут некоторый атрибут foo
, a позже строка может отменить это с !foo
. Это удаляет все настройки и возвращает foo
обратно в неопределенное состояние . Это полностью отличается от -foo
, который переводит foo в состояние unset .
(Не каждый атрибут, имеющий значение, фактически использует все эти различия.)