Как использовать git filter-branch для удаления файла с блоба SHA1?

Большинство примеров git filter-branch, которые я видел, которые удаляли файлы, были для удаления файлов на основе имени файла. Я не обязательно хочу сделать это. Вместо этого я идентифицировал несколько BLA-файлов (а не коммитов) файлов, которые я хочу удалить, независимо от того, где они находятся в хранилище. (Из-за нашей истории репо файлы, как правило, перемещаются в кучу без изменений.)

Как лучше всего сказать git filter-branch удалить файлы, основываясь на их BLA-объекте SHA1?

4 ответа

Решение

Ваша задача - удалить BLOB-объекты из истории Git по хеш-идентификатору. Вы можете найти это быстрее и проще использовать BFG, а не git-filter-branchспециально используя --strip-blobs-with-ids флаг:

-bi, --strip-blobs-with-id <blob-ids-file>... удалить BLOB-объекты с указанными идентификаторами объектов Git

Внимательно следуйте инструкциям по использованию, основная часть просто так:

$ java -jar bfg.jar  --strip-blobs-with-ids <blob-ids-file>  my-repo.git

Обратите внимание, что<blob-ids-file>Файл должен содержать идентификаторы объектов Git, а не простые SHA-1-хэши содержимого BLOB-объекта.

Для данного файла вы можете вычислить идентификатор объекта Git с помощьюgit hash-object:

$ git hash-object README.md
a63b49c2e93788cd71c81015818307c7b70963bf

Вы можете видеть, что это значение отличается от простого хэша SHA-1:

$ sha1sum README.md
7b833f7b37550e2df719b57e8c4994c93a865aa9  README.md

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

BFG обычно по крайней мере в 10-50 раз быстрее, чем бег git-filter-branchи вообще проще в использовании.

Полное раскрытие: я являюсь автором BFG Repo-Cleaner.

git filter branch --index-filter итеративно помещает каждый коммит в индекс, чтобы можно было восстановить имя файла из хеша с помощью git ls-files -s,

Я делаю это для удаления BLOB-объектов с хэшами 2d341f0223ff, 6a4558fa76d1 и 4d0a90cba061:

git filter-branch --force --index-filter "git ls-files -cdmo -s | grep ' 2d341f0223ff\| 6a4558fa76d1\| 4d0a90cba061' | awk '{print $4}' | xargs git rm --cached --ignore-unmatch 656565randomstring546464" --prune-empty --tag-name-filter cat -- --all

Случайная строка должна избежать git rm выдает ошибку, когда grep не возвращает совпадений.

Как отметил @RobertTyley в своем ответе, вам, вероятно, лучше использовать BFG. Тем не менее, чтобы ответить на вопрос, как задано (как это сделать с filter-branch):

К сожалению, нет хорошего пути. Вы можете написать скрипт, чтобы получить все имена файлов, связанные со значением SHA в индексе. В качестве отправной точки, если вы удаляете файл с хешем DEADC0DE

git rev-list -n 1 --objects HEAD |grep ^DEADC0DE |cut -c 42-

Затем вы будете кормить каждую строку (возможно, с xargs?) как <filename> в

git rm --cached <filename>

И вы бы использовали этот скрипт в качестве вашего index-filter значение (потому что использование его в качестве древовидного фильтра только сделает медленный подход еще медленнее).

Версия ветки фильтра может выглядеть примерно так внутри index-filter:

git ls-files -s |
  sed -r '/ 02c97746d64fbfe13007a1ab4e9b9e4bbd99f42f /s/^100(644|755)/0/' |
  git update-index --index-info

То есть, прочитайте формат index-info, найдите интересующий BLOB-объект и установите режим на 0 (пометив его для удаления), затем запишите его обратно в индекс.

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