Git статус: показывать определенные файлы как неизмененные, чтобы люди не добавляли и не фиксировали их - PullRequest
0 голосов
/ 16 марта 2020

Сценарий / цель:

  • мы используем сторонний инструмент визуального программирования для некоторых задач
  • этот инструмент изменяет множество файлов, даже если мы не вносим никаких регулярных изменений ( например, он обновляет атрибут временной метки внутри файла при открытии файла)
  • мы не хотим фиксировать файлы, в которых изменяются только атрибуты временных меток внутри файлов xml
  • мы используем оба Windows и Linux также среда разработки и различные инструменты для взаимодействия с репозиторием git

Идея:

  • Мы уже написали небольшой diff инструмент, который может решить, было ли изменение релевантным или не релевантным для нас
  • Мы знаем, как настроить "git diff", чтобы он использовал наш инструмент сравнения (через .gitattributes)

Проблема:

  • Возможно ли манипулировать "git status", чтобы он использовал difftool и не отображал файлы как измененные - в идеале не зависящие от операционной системы и используемого git клиент / UI?

1 Ответ

0 голосов
/ 16 марта 2020

Есть несколько способов, которые вы можете попробовать здесь, но clean фильтры, вероятно, способ go

Используя clean filter , вы можете удалить, или замените фиксированными константами, или как вам угодно, эти «шумовые» временные метки.

Фильтры очистки и удаления работают, когда файлы копируются между index и вашим рабочим деревом . В частности, чистые фильтры применяются ко всем копиям, которые переносят из рабочего дерева, в индекса. Текущий или HEAD коммит доступен всегда, если вы sh извлекаете из него информацию (см. Подробное объяснение исключения из этого правила).

По сути, что будет делать ваш чистый фильтр Фон Обратите внимание, что каждый коммит содержит полный и полный снимок все из ваших файлов. Коммиты - это не ревизии, это снимки. Другими словами, вы запрашиваете что-то другое, кроме файлов в вашем рабочем дереве . Это всегда возможно, потому что Git строит новые коммиты из index , а не из вашего рабочего дерева.

У вас довольно невоспитанный инструмент, который меняет несколько байтов внутри каждого файл каждый раз, когда вы просматриваете файл. Давайте на минутку возьмем это из картинки и просто посмотрим коммит Git и механизм git checkout. Представьте, что у вас есть очень маленький, недавно созданный репозиторий с тремя коммитами. Эти три коммита будут иметь несколько больших уродливых случайных идентификаторов ha sh, но для простоты мы назовем их A, B и C. Для конкретности скажем, что в коммите A есть только файл README.md, а ваши файлы сначала go в коммите B; Ваши файлы состоят из f1 и f2, а то, что находится в f2, отличается в коммитах B и C.

Вот чертеж состояния в этом крошечном хранилище с тремя коммитами и одна ветвь с именем master:

A <-B <-C   <--master

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 10 * * * * * * * * * * 10 * * * * * * * * * * * 10 * * * * * * * 10 * C содержит некоторые метаданные , такие как ваше имя и отметку даты и времени, когда вы сделали коммит C. Метаданные также включают в себя ваше сообщение журнала (из git commit -m или из вашего редактора, как бы вы его ни указывали) Важно отметить, что метаданные также содержат фактический идентификатор ha sh ранее commit B.

Commit B также содержит метаданные: ваше имя, когда вы его сделали, журнал сообщение и ha sh ID коммита A.

Коммит A, являющийся самым первым коммитом, просто опускает упоминание любого более раннего коммита. Нет более раннего коммита!

Мы говорим, что имя master указывает на C, C указывает на B, B указывает на A и A никуда не указывает.

Мы говорили, что в A есть только файл README.md. Файл внутри коммита A хранится в специальном, только для чтения, Git -только в замороженном и сжатом формате. Мне нравится называть эти замороженные снимки сублимированными версиями файлов. 1

Commit B share его копия README.md с коммитом A, так как он не изменился. В коммите B есть еще два файла: f1 и f2, ваши файлы.

Наконец, коммит C делится своей копией README.md с A и B и делится своей копией f1 с A. Если у Git есть замороженный снимок некоторых данных, Git может просто сохранить , повторно используя старый снимок. Это то, что делают новые коммиты: все они ссылаются на замороженные снимки, и если замороженный снимок - это тот, который никогда не появлялся ни в каком другом коммите, хорошо, этот замороженный снимок является новым для репозитория сейчас , но он может быть и будет предоставлен позже.

Просто для полноты примера давайте создадим новую ветку dev прямо сейчас, с master, указывающей на существующий коммит C. Мы получим этот график:

A--B--C   <-- master, dev

Оба Имена идентифицируют существующий коммит C. Теперь нам нужно еще кое-что на нашей картинке, это способ узнать какое название ветви мы используем. Оба идентифицируют коммит C, поэтому, если мы git checkout master или git checkout dev, мы получим коммит C, но это верно только сейчас. Итак, мы запустим git checkout dev, чтобы Git прикрепил специальное имя HEAD к одной ветви, например:

A--B--C   <-- master, dev (HEAD)

Теперь мы создадим new , который мы вызываем D, создавая или изменяя некоторый файл, запуская git add для этого файла и запуская git commit. Допустим, на этот раз мы обновили f1 и запустили git add f1 и git commit, чтобы получить D:

A--B--C   <-- master
       \
        D   <-- dev (HEAD)

Имя dev теперь идентифицирует коммит D , Коммит D указывает на существующий коммит C - тот, который вы использовали всего минуту назад - и имеет для своих снимков замороженные копии README.md, f1 и f2. Замороженный README.md остается таким же, как и во всех предыдущих фиксациях, и замороженный f2 используется совместно с C, в то время как замороженный f1 является новым для хранилища - он не соответствует этому в B или C.

Обратите внимание, что эти замороженные копии файлов в коммитах отлично подходят для архивирования, но бесполезны для выполнения какой-либо реальной работы, потому что они буквально не могут быть изменено. Так что Git должен где-то их извлечь, чтобы вы продолжали работать. Это где-то ваше рабочее дерево , которое содержит обычные файлы в обычном (не Git) формате. Это файлы, которые вы можете просматривать и работать с ними.


1 В техническом смысле даже внутри commit A не упрощает делиться. Зафиксировать A просто означает замороженный снимок. Сам коммит ссылается на Git объект дерева , который содержит имя файла, а затем объект дерева ссылается на Git объект BLOB-объекта , который содержит снимок, в стоп-кадре в сухом формате.


Замороженные снимки получены из index , а не из рабочего дерева

Причина всего вышеперечисленного фона - проиллюстрировать откуда взяты эти замороженные снимки. Когда вы запускаете git commit, Git создает новый коммит, но делает это из копий ваших файлов, которые находятся в Git index . Вы не можете видеть содержимое индекса, в любом случае, напрямую, 2 , но git status использует их для описания вещей.

Всегда Git имеет три копии каждого доступного файла или, точнее, до три копии. Одним из них является замороженная копия в текущем или HEAD коммите - коммите, который вы извлекли с помощью git checkout. Одним из них является обычный файл, который вы видите и работаете с ним. Но между этими двумя Git сохраняет третью копию, в Git index . (Указатель также называется областью подготовки , но я буду придерживаться здесь термина index . См. Также техническую заметку в сноске 2.)

Когда вы run git commit, Git не смотрит на ваше дерево работ. Вместо этого Git просто берет все файлы в индексе, которые уже хранятся в специальном Git только для чтения, только в лиофилизированном формате, и помещает их в новый коммит, который он создает. Это означает, что если вы каким-то образом изменили копию рабочего дерева и хотите обновить ее в новом снимке, вы должны сначала скопировать копию рабочего дерева в индекс . Это заменяет старую индексную копию новой.

Вот что git add делает: git add означает копирование файла рабочего дерева в индекс . Это , почему вам нужно git add файл, даже если он не новый . Вы должны обновить копию индекса .

Когда вы запускаете git checkout <name>, Git превращает имя ветви в коммит ha sh, чтобы найти фактический коммит. Затем, если это не текущий коммит, Git должен переключиться с текущего коммита на этот коммит. Git должен удалять файлы из индекса и вашего рабочего дерева по мере необходимости - если текущий коммит имеет, например, f3, а у цели нет - и копировать файлы целевого коммита в индекс и ваше рабочее дерево.

Следовательно, когда вы впервые переключаетесь на какую-то ветку или сначала извлекаете какую-то ветку, вы получаете один конкретный коммит - тот, который выбран по имени ветки - как HEAD коммит. Фиксированные файлы этого коммита go в индекс, так что индекс соответствует этому коммиту. Эти файлы повторно создаются для создания рабочего дерева, чтобы вы могли видеть и работать с этими файлами. Все три копии каждого файла теперь совпадают. 3

Когда вы запускаете git status, Git выполняет две отдельные операции git diff:

  • Первый сравнивает фиксацию HEAD с индексом. Если файлы здесь совпадают , git status ничего не говорит . Если они различаются, git status печатает имена разных файлов, вызывая их , подготовленные для фиксации .

  • Второй сравнивает индекс с рабочим деревом , Если файлы здесь совпадают , git status ничего не говорит . Если они различаются, git status печатает имена разных файлов, называя их не подготовленными для коммита .

Если все это имеет смысл, вы Теперь вы готовы понять - и написать - очистить и испачкать фильтры.


2 Вы можете использовать git ls-files - и в частности, git ls-files --stage, чтобы вывести сводку того, что находится в индексе. Если вы это сделаете, вы увидите, что, как и коммиты, индекс фактически хранит только файл names и ссылки на Git объекты BLOB-объектов .

Однако даже если в индексе хранится ссылка, а не фактическая копия, вы можете думать о ней как о копии. Он работает так же, просто если вы создаете большой двоичный объект, который в итоге не используется, Git должен собрать его позже. Обычно это не проблема: Git генерирует объекты мусора сам по себе, все время, и очищает их самостоятельно.

3 Существуют некоторые правила, позволяющие им не все совпадают на каждом git checkout: см. Извлечение другой ветки при наличии незафиксированных изменений в текущей ветке .


Очистить и размазать фильтры

Учитывая рисунки выше, мы теперь видим, что файлы перемещаются из коммита HEAD в индекс, а затем в дерево операций:

  HEAD          index         work-tree
---------      ---------      ---------
README.md  ->  README.md  ->  README.md

Это происходит в git checkout, а также в git reset --hard, например. В Git 2.23 и более поздних версиях git restore также может это делать.

По соображениям скорости и по другим причинам индексная копия всегда выполняется в формате сублимационной сушки Git only. Копия рабочего дерева - это обычный повседневный файл в обычном повседневном формате. Таким образом, процесс, который копирует из Git -форматированного формата в повседневный файл рабочего дерева, должен выполнить кучу работы по распаковке («регидратации») файла. Это может изменить файл. Что если Git позволит вам вставить здесь свои собственные операции «изменить файл»?

Между тем, git add делает это:

  HEAD          index         work-tree
---------      ---------      ---------
README.md      README.md  <-  README.md

Это заменяет индексную копию совершенно новой README.md. Команда git add должна сжать и Git определить файл - заморозить - dry как бы. Это может изменить то, что добавляется . Что если Git позволит вам внести свои собственные изменения здесь?

Это именно то, что представляют собой чистые и грязные фильтры. Вы можете вставить собственную фильтрацию в любом направлении:

  • A Фильтр смазывания принимает сжатый файл, Git ( что Git только что распаковано и де-1446 * -тифицировано) находится на пути к рабочему дереву. Вы можете вносить любые необходимые изменения в данные этого файла.

  • A clean filter принимает файл рабочего дерева - или, скорее, его содержимое - который находится в пути в указатель. Вы можете внести любые изменения в эти данные.

Следовательно, в вашем чистом фильтре вы можете удалить атрибуты XML или заменить их программно.

Смысл в том, что вы должны написать этот код самостоятельно. Вы уже сделали что-то очень похожее для вашего text-diff. Фильтры clean и smudge работают так же, как фильтры text-diff; они просто используются в другой части Git конвейеров. Вы устанавливаете эти фильтры с помощью .gitattributes и .git/config, так же, как вы делали с фильтрами text-diff.

Обратите внимание, что git status может иногда быть обманутым. Трудно определить, был ли файл изменен, просто взглянув на отметки даты и времени. Выполнение git checkout или git add будет, если Git считает, что файлы изменены, заставит данные проходить через различные фильтры и обновит информацию кеша, сохраненную в индексе, после чего Git снова примет, что работа древовидный файл «соответствует» индексной копии, несмотря на некоторое различие, которое было создано или устранено нечетким или чистым фильтром.

Особые случаи для рассмотрения

Если вы хотите установить определенные данные XML в индексная копия, совпадающая с копией HEAD, вам необходимо извлечь HEAD копию файла. Это означает, что вашим чистым и грязным фильтрам нужно будет координировать, потому что при извлечении копии HEAD ее содержимое будет проходить через грязный фильтр.

Переименованные файлы получают проблемы c здесь. Вы можете найти имя файла, используя %f, но это one name. Если файл был переименован, каково было старое имя?

Одна остающаяся слегка залипшая ситуация возникает в новом, абсолютно пустом хранилище. Здесь еще нет HEAD коммитов , потому что коммитов вообще нет. Этого легко избежать: не устанавливайте фильтры до тех пор, пока не будет начальная фиксация или если нет фиксации HEAD, пусть ваши фильтры ничего не делают. Обратите внимание, что такая же ситуация возникает при использовании git checkout --orphan: это переводит ваш Git в состояние, в котором HEAD содержит имя ветви, которая еще не существует, поэтому попытка преобразовать HEAD в коммит ha sh ID или извлечь файл из коммита с именем HEAD, не удастся. Вы можете сделать то же самое или просто запретить использование git checkout --orphan.

...