2013-02-07 2 views
14

В настоящее время у меня есть большой репозиторий git, который содержит много проектов, каждый из которых находится в своем подкаталоге. Мне нужно разбить его на отдельные репозитории, каждый проект в собственном репо.Как разбить репозиторий git и следовать переименованиям каталогов?

Я попытался git filter-branch --prune-empty --subdirectory-filter PROJECT master

Однако многие каталоги проекта прошли через несколько переименований в своей жизни, и git filter-branch не следует переименовывать так эффективно извлеченный репо не имеет никакой истории до последнего переименования.

Как я могу эффективно извлечь подкаталог из одного большого git-репо и следовать за всеми переименованиями этого каталога в прошлое?

ответ

8

Благодаря @Chronial, я был в состоянии приготовить сценарий массировать мой GIT репозиторий в соответствии с моими потребностями:

git filter-branch --prune-empty --index-filter ' 
    # Delete files which are NOT needed 
    git ls-files -z | egrep -zv "^(NAME1|NAME2|NAME3)" | 
     xargs -0 -r git rm --cached -q    
    # Move files to root directory 
    git ls-files -s | sed -e "s-\t\(NAME1\|NAME2\|NAME3\)/-\t-" | 
     GIT_INDEX_FILE=$GIT_INDEX_FILE.new \ 
     git update-index --index-info && 
     (test ! -f "$GIT_INDEX_FILE.new" \ 
      || mv -f "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE") 
' 

В основном то, что это делает это:

  1. Удаляет все файлы за пределами трех каталогов NAME1, NAME2 или NAME3, которые мне нужны (один проект был переименован в NAME1 -> NAME2 -> NAME3 за всю его жизнь).

  2. Перемещает все внутри этих трех каталогов в корень репозитория.

  3. Мне нужно было проверить, существует ли «$ GIT_INDEX_FILE.new», так как импорт svn в git создает коммиты без каких-либо файлов (только для каталогов). Нужно, только если репо было создано с помощью git svn clone.

+0

Просто для того, чтобы добавить к этому _amazing_ ответ от моей собственной борьбы - любому, кто использует Mac, нужно будет доработать GNU grep, sed и findutils фляги и заменить egrep gegrep, xargs с gxargs и sed с gsed соответственно. – thomasmichaelwallace

3

Я не думаю, что у git есть встроенная функция для этого. Вам нужно будет создать собственный фильтр. Просто используйте git filter-branch --prune-empty --tree-filter YOURSCRIPT. Затем ваш скрипт должен будет определить правильную папку (возможно, имя определенного файла в ней или, возможно, у вас есть список всех имен, которые этот проект имел в прошлом), удалить все остальное и переместить содержимое папки на уровень ,

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

0

У меня было очень большое хранилище, из которого мне нужно было извлечь одну папку; даже --index-filter, как было предсказано, займет 8 часов. Вот что я сделал вместо этого:

  1. Получить список всех прошлых имен папки. В моем случае было только два, old-name и new-name.
  2. Для каждого имени:

    $ git checkout master 
    $ git checkout -b filter-old-name 
    $ git filter-branch --subdirectory-filter old-name 
    

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

  3. filter-old-name филиал должен конец с коммита, который переименовал папку и filter-new-name ветвь должна начать с тем же совершить. (То же самое происходит, если было несколько переименований: вы закончите с эквивалентным количеством ветвей, каждый из которых будет делиться совместно с следующим.) Нужно удалить все, а другое снова заново создать его. Убедитесь, что эти два коммита имеют одинаковое содержимое; если они этого не делают, файл был изменен в дополнение к переименованию, и вам нужно будет объединить изменения. (В моем случае у меня не было этой проблемы, поэтому я не знаю, как ее решить.)

    Простой способ проверить это - попробуйте перезагрузить filter-new-name поверх filter-old-name, а затем раздавите два коммита вместе: git должен жаловаться, что это создает пустую фиксацию. (Обратите внимание, что вы захотите сделать это на резервной ветке, а затем удалить ее: rebasing удаляет данные коммиттера из коммитов, тем самым теряя часть истории, которую вы хотите сохранить.)

  4. Следующим шагом является трансплантация две ветви вместе, , пропуская две коммиты, которые переименовали папку. (В противном случае будет странный прыжок, где все будет удалено и воссоздано.) Это включает в себя поиск полной SHA (все 40 символов!) Двух коммитов и размещение их в информации git, с первым сообщением ветви , и старый имя ответвление второе.

    $ echo $NEW_NAME_SECOND_COMMIT_SHA1 $OLD_NAME_PENULTIMATE_COMMIT_SHA1 >> .git/info/grafts 
    

    Если вы сделали это правильно, git log --graph теперь должен показывать линию от конца новой истории до начала старой истории.

  5. Этот трансплантат временно является временным: он еще не является частью истории и не будет следовать вместе с клонами или толкает. Для того, чтобы сделать его постоянным:

    $ git filter-branch 
    

    Это refilter ветвь, не пытаясь делать какие-либо дальнейшие изменения, делая трансплантат постоянным (изменение всех фиксаций в filter-new-name ветви). Теперь вы можете удалить файл .git/info/grafts.

В конце всего этого, теперь вы должны иметь на filter-new-name ветви все истории из обоих имен папки. Затем вы можете использовать этот отдельный репозиторий или объединить его в другой, или что бы вы ни делали с этой историей.

 Смежные вопросы

  • Нет связанных вопросов^_^