Необходимо получить все различия между файлами (добавлены, изменены, переименованы) между двумя коммитами Git.

Я пытаюсь экспортировать все файлы с различиями между двумя коммитами, эти различия:

  • Новые файлы (добавлено)
  • Модифицированные файлы
  • Переименованные файлы
  • По возможности информация о любых удаленных файлах

Обнаружение переименований может быть трудным, так как я буду делать экспорт в среде Windows 7 и, следовательно, somefile.php такой же как SomeFile.php; но я буду загружать их в среду * nix, которая обрабатывает эти файлы как отличающиеся, поэтому их необходимо распознать и экспортировать, если это возможно.

Я использовал следующую команду:

git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT $head_commit_id $older_commit_id | xargs tar -cf project.tar -T -

Однако я заметил, что он не экспортирует новые / добавленные файлы, а также не экспортирует переименованные файлы; Затем я узнал, что git diff-tree по умолчанию не обнаруживает переименование, поэтому из того, что я вижу, мне нужно добавить --find-renames в команду?

2 ответа

Решение

Как и в ответе CodeWizard, вы можете использовать "удобную" (или фарфоровую) команду git diff вместо git diff-tree, что Git называет сантехнической командой, предназначенной для использования в скриптах. Однако вы должны знать, что это значит.

Поскольку фарфоровые команды предназначены для людей, они пытаются представить вещи в удобочитаемой форме. Это означает, что они подчиняются любым настройкам, которые один конкретный человек установил для себя в различных конфигурационных файлах. Это включает в себя diff.renames а также diff.renameLimit конфигурации. Они также могут изменять свои выходные данные, чтобы облегчить работу с глазными яблоками, но еще труднее для компьютерных программ. Хуже того, они могут изменить свой вывод с одной версии Git на другую, если кажется, что люди предпочитают какой-то вариант по умолчанию.

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

В конце концов, это означает, что если вы используете git diff-tree и установить правильные флаги, вы получите более надежный вывод. Если вы используете git diff Ваше обнаружение переименования зависит от:

Как вы обнаружили, вывод переименованного обнаружения - это два пути, которые вы не можете просто передать в архиватор. У архиваторов вообще есть проблемы с удалением файлов (возможно, это одно из классических различий между архивами и резервными копиями / снимками; обратите внимание, что оба они также связаны с контролем версий).

Если ваша цель - это некое объединение всех файлов, т. Е. Если diff говорит, что файл называется A был добавлен, один по имени D был удален, и файл R был создан путем переименования старого имени O (и, возможно, также изменив его: обратите внимание, индекс индекса сходства Git, который идет после буквы R), то вы хотите собрать файл A, игнорировать файл D и собрать файл R игнорируя файл O - ну, тогда, во-первых, вы не хотите переименовывать! Если вы не обнаружите переименования, которое git diff-tree не по умолчанию - этот же diff будет представлен как: добавить файл A, удалить файл D, удалить файл O и добавить файл R, Так что git diff-tree с diff-filter это включает AM и исключает D достаточно. Менее понятно, что делать с T, который предназначен для изменения типа: например, из обычного файла в символьную ссылку или из файла в хеш фиксации суб-репозитория (что Git называет записью gitlink для субмодуля).

Точно так же вы не хотите включать обнаружение копирования: C статус, как R, представляет индекс сходства и пару путей. Если вы оставите его отключенным, вы просто получите новый путь как A ддед файл.

Даже если вы делаете все это, вы все равно настроены на ловушку. Предположим, что хеш коммита C1 имеет файл с именем problem и (предположительно позже) хеш коммита C2 имеет вместо этого два файла с именем problem/A а также problem/B, Это означает, что оригинальный файл problem был удален между этими двумя точками, потому что большинство систем (включая сам Git) запрещают иметь оба файла с именем problem и каталог с именем problem держа различные файлы. Учитывая, что каждый tar-архив сам по себе не является полным снимком - вы опускаете файлы, которые не были изменены между C1 и C2, - ваша процедура извлечения этих снимков обязательно должна быть аддитивной: извлечь более ранний снимок, а затем извлечь более поздний снимок поверх более раннего снимка. Этот процесс не удастся в точке, где файл problem находится в пути создания каталога problem, Очевидно, что вы можете проверить наличие таких проблем и удалить проблемный файл (теперь вы можете понять, почему я назвал файл problem:-)), но в более общем плане, поскольку вы не храните директивы "delete" в первую очередь, вы не будете знать, что в будущем, когда вы будете использовать эти архивы для восстановления снимка, некоторые файлы этого не делают. принадлежат в этом снимке вообще.

(Классическое решение этой проблемы заключается в добавлении префикса update-archives к какому-либо манифесту или директиве. Если вы решите использовать такое решение, то, в зависимости от типа деталей, которые вы хотите указать в манифесте или директиве, вы можете хотите сделать первый проход для определения точных переименований и / или точных копий.)


1 Очевидно, что недавно добавленные функции могут представлять проблемы для всех, не только для сценариев и не только для людей, но и для людей из Git, которые работают над тем, чтобы не создавать ненужных проблем для сценариев, использующих сантехнические команды. Рассмотрим, например, новый стимул подтолкнуть Git к использованию некоторой разновидности SHA-256 вместо или в дополнение к SHA-1. Поскольку SHA-1 создает 160-битные хэши, а SHA-256 создает 256-битные хэши, они должны быть представлены в виде 40 и 64 шестнадцатеричных цифр соответственно. Линус предложил сокращать 256-битные хэши до 40 символов по умолчанию, чтобы помочь существующим сценариям, которые принимают 40 символов, но я предвижу некоторые проблемы...:-)

Почему бы не использовать эту простую команду статуса:

git diff --name-only SHA1 SHA2
# or

# --name-status will display the name and the status of the files
git diff --name-status SHA1 SHA2

# To display untracked files use the -u
git status -u

И в Git вы должны переименовывать файлы только с git mv команда.

Другие вопросы по тегам