Как извлечь один файл с историей коммитов из git-репо с помощью index-filter & co - PullRequest
33 голосов
/ 11 сентября 2011

Моя ситуация была, у меня есть git-репозиторий, преобразованный из SVN в HG в GIT, и я хотел извлечь только один исходный файл.У меня также были странные символы, такие как aÌ (несоответствие кодировки повреждено Unicode ä) и пробелы в именах файлов.

Кажется, это не особенно легко, и именно поэтому я отвечу на свой собственный вопрос, несмотря на множество подобных вопросов, касающихсяgit [index-filter | subdirectory-filter | filter-tree], так как мне нужно было использовать все предыдущее, чтобы добиться этого!

Итак, вопрос: «Как я могу извлечь один файл из репозитория и поместитьэто в корне нового репо? "

Ответы [ 4 ]

38 голосов
/ 04 мая 2016

Более быстрый и простой для понимания фильтр, который выполняет то же самое:

git filter-branch --index-filter '
                        git read-tree --empty
                        git reset $GIT_COMMIT -- $your $files $here
                ' \
        -- --all -- $your $files $here
13 голосов
/ 11 сентября 2011

Сначала короткое замечание, что даже заклинание, как в комментарии к Разделение набора файлов внутри git-репо в их собственное хранилище, сохраняющее соответствующую историю

SPELL='git ls-tree -r --name-only --full-tree "$GIT_COMMIT" | grep -v "trie.lisp" | tr "\n" "\0" | xargs -0 git rm --cached -r --ignore-unmatch'
git filter-branch --prune-empty --index-filter "$SPELL" -- --all

будетне помогает с файлами с именем вроде imaging/DrinkkejaI<0300>$'\302\210'.txt_74x2032.gif.Часть aI<0300>$'\302\210' когда-то представляла собой одну букву: ä.

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

git filter-branch -f --subdirectory-filter lisp/source/model HEAD

В качестве альтернативы, вы можете использовать --tree-filter: (тест необходим, потому что файл был в другом каталоге ранее, см .: Как я могу переместить каталог в репозитории Git для всех коммитов? )

MV_FILTER='test -f source/model/trie.lisp && mv ./source/model/trie.lisp . || echo "Nothing to do."'
git filter-branch --tree-filter $MV_FILTER HEAD --all

Чтобы увидеть все имена файлов, используйте:

git log --pretty=oneline --follow --name-only git-path/to/file | grep -v ' ' | sort -u

Как описано в http://whileimautomaton.net/2010/04/03012432

Также выполните следующие шаги:

$ git reset --hard
$ git gc --aggressive
$ git prune
$ git remote rm origin # Otherwise changes will be pushed to where the repo was cloned from
10 голосов
/ 12 мая 2014

Обратите внимание, что все становится намного проще, если вы комбинируете это с дополнительным этапом перемещения нужного файла (ов) в новый каталог.

Это может быть довольно распространенным вариантом использования (например, перемещение нужного сингла).файл в корневой каталог).
Я сделал это (используя git 1.9) примерно так (сначала переместив файл (ы), затем удалив старое дерево):

git filter-branch -f --tree-filter 'mkdir -p new_path && git mv -k -f old_path/to/file new_path/'
git filter-branch -f --prune-empty --index-filter 'git rm -r --cached --ignore-unmatch old_path'

Вы можете даже легко использоватьподстановочные знаки для нужных файлов (без возни с grep -v).

Я думаю, что это ('mv' и 'rm') также может быть сделано в одной ветви-фильтра, но это не такработать для меня.

Я не пробовал это с странными персонажами, но я надеюсь, что это все равно поможет.Делать вещи проще, мне всегда кажется хорошей идеей.

Подсказка:
Это длительное действие на больших репозиториях.Поэтому, если вы хотите выполнить несколько действий (например, получить кучу файлов, а затем переставить их в 'new_path / subdirs'), неплохо было бы выполнить часть 'rm' как можно скорее, чтобы получить меньшее и более быстрое дерево.

0 голосов
/ 28 мая 2019

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

FILES='path/to/file1 other-path/to/file2 file3'
git filter-branch --prune-empty --index-filter "
                        git read-tree --empty
                        git reset \$GIT_COMMIT -- $FILES
                " \
        -- --all -- $FILES

Затем вы можете объединить эту новую ветку с вашим целевым репозиторием с помощью обычных команд merge или rebase в соответствии с вашим вариантом использования.

...