Удалите историю для всего, кроме списка файлов, используя git filter-branch

Я пытаюсь переместить некоторые файлы между двумя репозиториями git repo1 и repo2. У меня есть небольшой список файлов, которые я хотел бы переместить (с сохранением истории).

Три файла для перемещения из repo1:

libraryname/file1
libraryname/file2
tests/libraryname/file3

В libraryname/ и tests/libraryname/ есть другие файлы. В / и tests/ есть другие папки

Мой план состоит в том, чтобы извлечь repo1, а затем изменить дерево истории, пока оно не будет содержать историю только для интересующих меня файлов. Затем извлечь repo2 и объединить выходные данные предыдущей операции. Кажется, что git filter-branch — правильный инструмент для первого шага.

До сих пор я пробовал git filter-branch --index-filter 'git rm -r --cached <FILES>' Где <FILES> перечислены все нежелательные целые папки или файлы.

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

Как мне получить дерево коммитов git, которое включает только эти три файла? Есть ли лучший способ, чем я предлагаю? Или есть способ удалить следы всех файлов, которые в настоящее время не существуют в HEAD?


person Adam Casey    schedule 11.08.2017    source источник


Ответы (3)


Вы сказали, что после него остаются папки; Я предполагаю, что вы имеете в виду, что он оставляет файлы in в этих папках (потому что git не сохраняет пустые папки)...

Похоже, вы можете захотеть очистить индекс, а затем повторно добавить нужные записи.

git filter-branch ...
    --index-filter 'git rm -r --cached * && git reset $GIT_COMMIT -- libraryname/file1 libraryname/file2 tests/libraryname/file3
    ...

Поскольку вы так сильно сокращаете содержание, не забывайте, что вы можете включить параметр --prune-empty.

person Mark Adelsberger    schedule 11.08.2017
comment
Это сработало для меня, когда git checkout $GIT_COMMIT <path> не удалось (для коммитов, в которых <path> не существует). Благодарю вас! - person bossi; 01.11.2018

В Git 2.24 (4 квартал 2019 г.) git filter-branch устарело.

Эквивалентным было бы использование newren/git-filter-repo и его пример раздела:

Если у вас есть длинный список файлов, каталогов, шаблонов или регулярных выражений для фильтрации, вы можете поместить их в файл и использовать --paths-from-file; например, с файлом с именем stuff-i-want.txt с содержимым

README.md
guides/
tools/releases
glob:*.py
regex:^.*/.*/[0-9]{4}-[0-9]{2}-[0-9]{2}.txt$
tools/==>scripts/
regex:(.*)/([^/]*)/([^/]*)\.text$==>\2/\1/\3.txt

тогда вы могли бы бежать

git filter-repo --paths-from-file stuff-i-want.txt

В вашем случае stuff-i-want.txt будет:

libraryname/file1
libraryname/file2
tests/libraryname/file3

Как указывает kubanczyk, в комментариях:

Хорошо работает на Ubuntu 20.04, вы можете просто pip3 install git-filter-repo, так как он предназначен только для stdlib и не устанавливает никаких зависимостей.

В Ubuntu 18 он несовместим с git-версией дистрибутива, но его достаточно легко запустить на docker run -ti ubuntu:20.04

person VonC    schedule 05.10.2019
comment
Хороший инструмент! Хорошо работает на Ubuntu 20.04, вы можете просто pip3 install git-filter-repo, так как он предназначен только для stdlib и не устанавливает никаких зависимостей. В Ubuntu 18 он несовместим с git-версией дистрибутива, но его достаточно легко запустить на docker run -ti ubuntu:20.04 - person kubanczyk; 27.05.2020
comment
@kubanczyk Спасибо за отзыв. Я включил ваш комментарий в ответ для большей наглядности. - person VonC; 27.05.2020

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

  1. Создайте отсортированный список всех файлов во всех коммитах вашей ветки:

    $ export LC_COLLATE=C whitelist="$(mktemp)" && git log --name-status | sed 's/^[A-Z][[:space:]]\{1,\}//; t; d' | sort -u > "$whitelist"

  2. Отредактируйте этот список в своем любимом текстовом редакторе и удалите все файлы, которые не представляют интереса для сохранения, т.е. е. создать белый список файлов, которые нужно сохранить.

    $ "$EDITOR" -- "$whitelist" # remove from list what you don't want to keep

  3. Выполните фактическую операцию фильтра:

    $ git filter-branch -f --index-filter 'git ls-files -c | sort | comm -23 -- - "$whitelist" | while IFS= read -r f; do git rm --cached -- "$f"; done' --prune-empty

  4. Удалите белый список после того, как работа фильтра прошла без проблем.

    $ rm -- "$whitelist" && unset LC_COLLATE whitelist

person Guenther Brunthaler    schedule 17.03.2018