Перенос части истории репозитория git в другой репозиторий

Здесь много сообщений о перемещении папки из одного репозитория в новый репозиторий с помощью git filter-branch; мне нужно переместить единственный файл в новый репозиторий.

Я уже создал новый репозиторий, добавил старый из файловой системы как «удаленный» и создал новую «корневую фиксацию» (просто добавив README для нового однофайлового проекта). Теперь мне нужно трансплантировать коммиты, относящиеся к этому конкретному файлу, в этот новый корневой коммит.

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


person ELLIOTTCABLE    schedule 21.02.2011    source источник


Ответы (4)


Это основано на примере на странице руководства filter-branch:

git filter-branch --index-filter 'git ls-files -s | grep $'\t'<file-to-keep>$ | \
    GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && \
    mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' --prune-empty -- --all

Фильтр индекса печатает текущее содержимое индекса с помощью git ls-files -s, выделяет только файл, который нужно сохранить (этот grep довольно навязчивый - поля разделены табуляцией, при этом имя файла является последним), затем создает новый индекс, используя этот информации и перемещает ее поверх старой.

Параметр --prune-empty заставляет filter-branch удалять любые коммиты, которые теперь ничего не делают (т.е. они касались только других файлов), а -- --all сообщает ему переписать все ссылки.

Как всегда с filter-branch, лучше всего сделать это в свежем клоне, поэтому, если вы что-то сильно испортите, вы в безопасности, даже если filter-branch действительно хранит резервные копии в refs / originals. Вы также можете прочитать контрольный список для сжатия репозитория на странице руководства. ; В результате лучший способ избавиться от всего, что вам больше не нужно, - это просто клонировать отфильтрованный репозиторий.

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

(Примечание: проще удалить отдельный файл, чем сохранить отдельный файл. Все, что вам нужно сделать в этом случае, - это использовать git rm --cached --ignore-unmatch <filename> для фильтра индекса.)

person Cascabel    schedule 21.02.2011
comment
Выглядит отлично! Я запрошу его, как только приду домой, и приму ваш ответ, если он сработает. ^ _ ^ Я ценю ваше время. - person ELLIOTTCABLE; 24.02.2011
comment
Хм, не получается: Rewrite bb6a446cc395e38cdcb7af7953b151e0a706929d (1/15)mv: /Users/elliottcable/Code/Cest.c/.git-rewrite/t/../index.new: No such file or directory - person ELLIOTTCABLE; 27.02.2011
comment
@elliottcable: Ага, я уже получал это раньше, и я не помню, в чем причина этого и как это исправить. Я тестировал это на репо, и это сработало ... но пока я работал над ошибками, я получал эту ошибку пару раз. Я думаю, что во время перенастройки возникла путаница с рабочим каталогом. Так как вы позаботились об этом по-другому, я не думаю, что собираюсь пытаться понять это прямо сейчас. - person Cascabel; 27.02.2011
comment
В любом случае спасибо за ваше время. : D - person ELLIOTTCABLE; 28.02.2011

В итоге я использовал ответы из этого (в остальном не относящегося к делу) сообщения, чтобы построить решение. Вместо того, чтобы преобразовать дерево в новый корень, я изменил старый корень и --onto перебазировал содержимое в новый корень:

Могу ли я удалить начальную фиксацию из Репозиторий Git?

person ELLIOTTCABLE    schedule 27.02.2011

Решение в другом ключе - добавьте старое репо в качестве другого пульта дистанционного управления, и тогда можно будет выполнить перебазирование и выбор вишни.

git clone old_repo
git clone new_repo
cd new_repo
git remote add temp_repo old_repo
git fetch temp_repo

# bring the changed from old_repo into new_repo
git rebase --onto <...> temp_repo/master
OR
git cherry-pick [list of relevant commits]

git remote rm temp_repo
git push origin HEAD
person orip    schedule 05.02.2013

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

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

Первый этап

  1. Сделайте копию репозитория A, поскольку следующие шаги вносят в эту копию существенные изменения, которые вы не должны продвигать!

    git clone --branch <branch> --origin origin --progress -v <git repository A url>
    eg. git clone --branch master --origin origin --progress -v https://username@giturl/scm/projects/myprojects.git
    

    (при условии, что myprojects - это репозиторий, из которого вы хотите скопировать)

  2. cd в это

    cd <git repository A directory>          eg. cd /c/Working/GIT/myprojects
    
  3. Удалите ссылку на исходный репозиторий, чтобы случайно не внести какие-либо удаленные изменения (например, нажав)

    git remote rm origin
    
  4. Просмотрите свою историю и файлы, удалив все, что не находится в каталоге 1. В результате содержимое каталога 1 перенесено в базу репозитория A.

    git filter-branch --subdirectory-filter <directory> -- --all
    eg. git filter-branch --subdirectory-filter subfolder1/subfolder2/FOLDER_TO_KEEP -- --all
    
  5. Только для перемещения одного файла: просмотрите то, что осталось, и удалите все, кроме желаемого файла. (Возможно, вам придется удалить ненужные файлы с тем же именем и зафиксировать.)

    git filter-branch -f --index-filter \
    'git ls-files -s | grep $'\t'FILE_TO_KEEP$ |
    GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
    git update-index --index-info && \
    mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE || echo "Nothing to do"' --prune-empty -- --all
    

    например. FILE_TO_KEEP = pom.xml, чтобы сохранить только файл pom.xml из FOLDER_TO_KEEP

Второй этап

  1. Шаг очистки

    git reset --hard
    
  2. Шаг очистки

    git gc --aggressive
    
  3. Шаг очистки

    git prune
    

Вы можете импортировать эти файлы в репозиторий B в каталог, а не в корень:

  1. Сделайте этот каталог

    mkdir <base directory>             eg. mkdir FOLDER_TO_KEEP
    
  2. Переместить файлы в этот каталог

    git mv * <base directory>          eg. git mv * FOLDER_TO_KEEP
    
  3. Добавить файлы в этот каталог

    git add .
    
  4. Зафиксируйте свои изменения, и мы готовы объединить эти файлы в новый репозиторий.

    git commit
    

Этап третий

  1. Сделайте копию репозитория B, если у вас его еще нет

    git clone <git repository B url>
    eg. git clone https://username@giturl/scm/projects/FOLDER_TO_KEEP.git
    

    (при условии, что FOLDER_TO_KEEP - это имя нового репозитория, в который вы копируете)

  2. cd в это

    cd <git repository B directory>          eg. cd /c/Working/GIT/FOLDER_TO_KEEP
    
  3. Создайте удаленное соединение с репозиторием A как ветку в репозитории B

    git remote add repo-A-branch <git repository A directory>
    

    (репо-A-ветка может быть любым - это просто произвольное имя)

    eg. git remote add repo-A-branch /c/Working/GIT/myprojects
    
  4. Вытяните из этой ветки (содержащей только каталог, который вы хотите переместить) в репозиторий B.

    git pull repo-A-branch master
    

    При извлечении копируются как файлы, так и история. Примечание: вы можете использовать слияние вместо вытягивания, но вытягивание работает лучше.

  5. Наконец, вы, вероятно, захотите немного очистить, удалив удаленное соединение с репозиторием A

    git remote rm repo-A-branch
    
  6. Нажмите, и все готово.

    git push
    
person mcarans    schedule 28.03.2014