Как передать программно сгенерированный список файлов в `git filter-branch`?
Я разделяю часть git-репо, чтобы создать новый репо, и пытаюсь использовать git filter-branch
вести историю файлов, которые перемещаются в новый проект. Я знаю о --subdirectory-filter
но это не очень хорошее решение, потому что файлы, которые я извлекаю, не отображаются точно в один подкаталог. Лучший вариант, который я нашел, это --index-filter
, используется следующим образом:
git filter-branch -f --index-filter 'git read-tree --empty && git reset -q "${GIT_COMMIT}" -- <list of files>' --prune-empty -f
Кажется, это работает, за исключением того, что я хотел бы иметь возможность программно генерировать список файлов для хранения, чтобы я мог итеративно уточнить этот список. В настоящее время я пытаюсь получить список файлов, которые я хочу сохранить в другом файле, и добавить его к строке, представляющей команду, которая будет выполняться для каждого коммита, следующим образом:
tmp=$(cat ~/to_keep.txt) && git filter-branch -f --index-filter 'git read-tree --empty && git reset -q "${GIT_COMMIT}" -- '$tmp --prune-empty -f
К сожалению, это приводит к
fatal: bad flag '--prune-empty' used after filename
Кажется, даже простое отображение файлов вызывает проблемы:
tmp=$(echo a.txt b.txt) && git filter-branch -f --index-filter 'git read-tree --empty && git reset -q "${GIT_COMMIT}" -- '$tmp --prune-empty -f
fatal: ambiguous argument 'b.txt': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
Я также попытался объединить строки ранее:
tmp1=$(echo a.txt b.txt) && tmp2='git read-tree --empty && git reset -q "${GIT_COMMIT}" -- ' && tmp3=${tmp2}${tmp1} && git filter-branch -f --index-filter $tmp3 --prune-empty -f
fatal: ambiguous argument 'read-tree': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
Я предполагаю, что это просто конкатенация не происходит, как я ожидаю в оболочке. Кто-нибудь знает, как я могу сделать эту работу? Было бы здорово, если бы вы могли объяснить, что означают и эти ошибки. Благодарю.
1 ответ
Каждый аргумент к различным ...-filter
s должен быть одной строкой. Эта строка сохраняется как переменная оболочки:
--index-filter)
filter_index="$OPTARG"
;;
В соответствующий момент сценарий ответвления фильтра (находится в git-core
подкаталог, например, /usr/libexec/git-core
или же /usr/local/libexec/git-core
) Является ли это:
eval "$filter_index" < /dev/null ||
die "index filter failed: $filter_index"
(за исключением коммит-фильтра, который запускается с /bin/sh -c "$filter_commit" ...
).
Таким образом, ваше предположение верно, и вам нужно, чтобы список файлов был частью одной строки, разделенной пробелами.
Самый простой способ сделать это - начать с исходной команды:
git filter-branch -f --index-filter \
'git read-tree --empty && git reset -q "${GIT_COMMIT}" -- <list of files>' \
--prune-empty -f
(который работает, когда у вас есть статический список) и измените его, чтобы извлечь динамический список из ~/to_keep.txt
, Я разделил оригинал на три строки частично для целей отображения, а также потому, что теперь мы можем сосредоточиться только на средней строке.
[Редактировать, чтобы исправить проблему новой строки, отмеченную в комментарии. Давайте сделаем псевдоним или функцию оболочки, xc
, переводит переводы строк в пробелы]
xc() {
tr '\n' ' '
}
"git read-tree --empty && git reset -q \"\${GIT_COMMIT}\" -- $(xc < ~/to_keep.txt)" \
или же:
'git read-tree --empty && git reset -q "${GIT_COMMIT}" -- '"$(xc < ~/to_keep.txt)" \
или, как вы пытались (но с одним изменением):
'git read-tree --empty && git reset -q "${GIT_COMMIT}" -- '"$tmp" \
(установив tmp=$(xc < ~/to_keep.txt)
).
Обратите внимание, что ни одна из этих правильных вещей, если любое из имен файлов содержит пробелы. Например, предположим, что файл называется a file
(со встроенным бланком). eval
будет разбивать аргументы в пробелах, а git reset
Команда получит имена a
а также file
как два отдельных аргумента.
Пока у вас нет таких имен файлов, вам не нужно беспокоиться об этом.
Еще одна потенциальная проблема, если этот список файлов становится очень длинным. Вы можете столкнуться с ограничениями ядра на количество аргументов, которые могут быть отправлены в один файл. Вы должны быть в состоянии использовать xargs
чтобы решить это (и, в этом отношении, с некоторой работой и использованием -0
, для обработки пробелов в именах файлов).