Перепишите историю репозитория git, удалив все файлы, кроме foo и bar.

Я хочу удалить все файлы, которые не являются ни «foo», ни «bar» в истории репозитория git. Учитывая, что rm -rf !(foo|bar) работает для удаления файлов в текущем каталоге, которые не являются ни «foo», ни «bar». Мне пришла в голову следующая идея:

$ git filter-branch --tree-filter 'rm -rf !(foo|bar)' --prune-empty HEAD

Однако это вызвало следующую ошибку, которую я действительно не понимаю, почему она не работает.

Rewrite 75c1ec1ef083338ca2e88db9cc6107c1630d91e9 (1/871)~/libexec/git-core/git-filter-branch: 1: eval: Syntax error: "(" unexpected


person York    schedule 13.07.2014    source источник
comment
Почему бы не перечислить все файлы, которые не являются ни foo, ни bar? Я не думаю, что вы хотите запускать rm в этом контексте.   -  person Makoto    schedule 14.07.2014
comment
@ Макото: Не понимаю, что вы имели в виду. Чего я точно хотел, так это удалить все файлы в репозитории (удалить их во всей истории), оставив только файлы foo и bar.   -  person York    schedule 14.07.2014


Ответы (3)


см. https://stackoverflow.com/a/19957874/1870481

Либо используйте

--tree-filter 'bash preserve-only.sh foo bar'

с preserve-only.sh

IFS=':'
GLOBIGNORE="$*"
rm -rf *

или используя

--index-filter \
'git rm --cached -r -q -- . ; git reset -q $GIT_COMMIT -- foo bar' \
 --prune-empty

Последнее особенно приятно. Просто удалите все и восстановите то, что хотите. :)

person michas    schedule 13.07.2014
comment
Этот индексный фильтр также будет намного быстрее. - person torek; 14.07.2014
comment
@michas: --index-filter 'git rm --cached -r -q -- . ; git reset -q $GIT_COMMIT -- foo bar' сработало, и это хорошо. Но нужно добавить --prune-empty, чтобы удалить пустые коммиты, которые больше не актуальны. Хотя было бы еще лучше, если бы вы могли дать объяснение. В любом случае, отличный ответ и большое спасибо. - person York; 14.07.2014

У меня это работает, используя следующий код:

git filter-branch -f --tree-filter "bash -O extglob -c 'rm -rf ./path/to/!(FILENAME_TO_KEEP)'" HEAD

В частности, передача -O extglob в bash позволяет вам использовать синтаксис с восклицательным знаком.

person adzenith    schedule 06.04.2016

Непосредственная проблема заключается в том, что !(foo|bar) — это синтаксис, специфичный для zsh [Изменить: или bash с включенным extglob], не поддерживаемый в sh. Каждый фильтр запускается с eval сценарием оболочки, который реализует git-filter-branch (обычно обычный sh).

Конечно, вы можете вызвать zsh (или команду bash, которая включает extglob) напрямую для запуска команды. Или вы можете удалить ненужные файлы по их (плохим) именам, а не по тому, что у них нет хороших имен.

(Чтобы удалить файлы с filter-branch, намного быстрее использовать --index-filter, который не требует проверки каждой фиксации и повторной фиксации. Но это внутренняя оптимизация, и только 871 фиксация не так важна.)

person torek    schedule 13.07.2014
comment
Да, это поддерживается bash extglob. Использование shopt для включения этого расширенного сопоставления с образцом. В моей Ubuntu 14.x он включен по умолчанию. - person York; 14.07.2014
comment
А, я вижу, это есть в bash с установленным extglob. Однако сценарий запускается либо с помощью простого sh, либо с помощью bash без установки extglob. В любом случае он недоступен по умолчанию. - person torek; 14.07.2014
comment
Вы имеете в виду, что даже если echo !(foo|bar) дает ожидаемые результаты, его использование в git-filter-branch все равно не сработает? Если да, то почему? - person York; 14.07.2014
comment
Да: потому что вы делаете это из процесса оболочки командной строки, в то время как скрипт git filter-branch использует другой экземпляр процесса (и даже другую оболочку, /bin/sh, в системах Linux). - person torek; 14.07.2014
comment
Значит, это не вызывает bash, верно? Во всяком случае, чтобы заставить его работать еще? И да, индексный фильтр был намного быстрее. - person York; 14.07.2014
comment
Да, вы можете заставить его работать, запустив свой собственный отдельный скрипт bash (с включенным extglob), как в ответе michas. Если на то пошло, вы можете написать программу на C, Java или Python и указать filter-branch вызывать эту программу из древовидного фильтра. Смысл eval в скрипте в том, что он позволяет вам использовать другие вещи, которые есть в этом скрипте. Чтобы действительно понять это, вы должны взглянуть на сам скрипт git-filter-branch. - person torek; 14.07.2014