Отключить (переместить) подкаталог в отдельный репозиторий Git

У меня есть репозиторий Git, который содержит несколько подкаталогов. Теперь я обнаружил, что один из подкаталогов не связан с другим и должен быть отделен от отдельного репозитория.

Как я могу это сделать, сохранив историю файлов в подкаталоге?

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

Чтобы было понятно, у меня есть следующая структура:

XYZ/
    .git/
    XY1/
    ABC/
    XY2/

Но вместо этого я бы хотел это:

XYZ/
    .git/
    XY1/
    XY2/
ABC/
    .git/
    ABC/

person matli    schedule 11.12.2008    source источник
comment
Теперь это тривиально, git filter-branch см. Мой ответ ниже.   -  person jeremyjjbrown    schedule 20.08.2014
comment
@jeremyjjbrown прав. Это уже не сложно сделать, но трудно найти правильный ответ в Google, потому что все старые ответы преобладают в результатах.   -  person Agnel Kurian    schedule 14.10.2014


Ответы (26)


Обновление. Этот процесс настолько распространен, что команда git упростила его с помощью нового инструмента git subtree. См. Здесь: Отключить (переместить) подкаталог в отдельный репозиторий Git


Вы хотите клонировать свой репозиторий, а затем использовать git filter-branch, чтобы отметить все, кроме подкаталога, который вы хотите в новом репозитории, для сбора мусора.

  1. Чтобы клонировать локальный репозиторий:

    git clone /XYZ /ABC
    

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

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

    cd /ABC
    for i in branch1 br2 br3; do git branch -t $i origin/$i; done
    git remote rm origin
    

    или для всех удаленных филиалов:

    cd /ABC
    for i in $(git branch -r | sed "s/.*origin\///"); do git branch -t $i origin/$i; done
    git remote rm origin
    
  3. Теперь вы можете также удалить теги, не имеющие отношения к подпроекту; вы также можете сделать это позже, но вам, возможно, придется снова сократить репо. Я не сделал этого и получил WARNING: Ref 'refs/tags/v0.1' is unchanged для всех тегов (поскольку все они не имели отношения к подпроекту); Кроме того, после удаления таких тегов будет освобождено больше места. Очевидно, git filter-branch должен иметь возможность перезаписывать другие теги, но я не смог это проверить. Если вы хотите удалить все теги, используйте git tag -l | xargs git tag -d.

  4. Затем используйте filter-branch и reset, чтобы исключить другие файлы, чтобы их можно было удалить. Давайте также добавим --tag-name-filter cat --prune-empty, чтобы удалить пустые коммиты и перезаписать теги (обратите внимание, что это должно будет удалить их подпись):

    git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC -- --all
    

    или, в качестве альтернативы, просто переписать ветку HEAD и игнорировать теги и другие ветки:

    git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC HEAD
    
  5. Затем удалите резервные журналы рефлогов, чтобы можно было по-настоящему освободить пространство (хотя теперь операция разрушительна).

    git reset --hard
    git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
    git reflog expire --expire=now --all
    git gc --aggressive --prune=now
    

    и теперь у вас есть локальный репозиторий git подкаталога ABC со всей его историей.

Примечание. В большинстве случаев git filter-branch действительно должен иметь добавленный параметр -- --all. Да, это действительно - - space - - all. Это должен быть последний параметр команды. Как обнаружил Матли, при этом ветки и теги проекта остаются включенными в новое репо.

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

person Community    schedule 11.12.2008
comment
Хорошая точка зрения! Это применимо к большинству людей, использующих git filter-branch таким образом. В моем случае я разделял библиотеку, у которой была собственная серия номеров выпуска в тегах, поэтому мне не нужны теги проекта в моем новом репо. Я отредактирую это в ответ. - person Paul; 15.12.2008
comment
На сегодняшний день фильтрация-ветвь не поддерживается в Windows. Похоже, это скоро произойдет. За подробностями обращайтесь к группе обсуждения msysgit (в группах Google). - person Osman; 01.04.2009
comment
Зачем тебе --no-hardlinks? Удаление одной жесткой ссылки не повлияет на другой файл. Объекты Git тоже неизменяемы. Только если вы измените права владельца / файла, вам понадобится --no-hardlinks. - person vdboor; 01.02.2010
comment
git-filter-branch set_ident () вызывает LANG=C LC_ALL=C sed ... для максимальной совместимости с sed, не поддерживающим UTF-8. Следовательно, git-filter-branch умирает при коммитах с символами UTF-8 в полях author: или committer: (см. git show --pretty=raw <commit SHA1>). Обходной путь: используйте sed, поддерживающий UTF-8 (например, GNU sed), и отредактируйте git-filter-branch: LANG=en_US.UTF-8 sed -ne ... - person Richard Michael; 20.08.2010
comment
Также еще не включенный «git filter-branch --split-submodule» можно использовать для сохранения согласованной истории. vi-server.org/vi/bin/git-extract-submodule. патч - person Vi.; 27.12.2010
comment
Я считаю, что если репо /XYZ велико, репо /ABC не будет таким маленьким, как хотелось бы. Для дальнейшего сжатия: git clone /ABC /ABC.small ; cd /ABC.small ; git repack -A -d ; git gc --prune=DATE, где DATE - date +%Y-%m-%d. Почему-то, если я не git clone и работаю над клоном, ничего не сжимается. : - / - person jonp; 16.07.2011
comment
И если вы хотите переписать свои теги, чтобы не ссылаться на старую структуру, добавьте --tag-name-filter cat - person Malcolm Box; 06.10.2011
comment
Как и Пол, мне не нужны были теги проектов в моем новом репо, поэтому я не использовал -- --all. Я также запускал git remote rm origin и git tag -l | xargs git tag -d перед командой git filter-branch. Это уменьшило мой каталог .git с 60 МБ до ~ 300 КБ. Обратите внимание, что мне нужно было запустить обе эти команды, чтобы уменьшить размер. - person saltycrane; 18.11.2011
comment
Страница руководства git рекомендует вместо rm -rf .git/refs/original/, git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d; Я думаю, что последний более надежен, если ссылки не хранятся в нужном месте. Более того, я считаю, что «git remote rm origin» также необходим для сжатия репо, иначе ссылки из origin сохранят ссылки на объекты. @jonp, я думаю, это была проблема для тебя. Наконец, чтобы также перезаписать другие ветки, нужно настроить их вручную с помощью git branch после клонирования, -- --all и удаления HEAD (что останавливает перезапись других ветвей). - person Blaisorblade; 20.02.2012
comment
Для многих читателей (включая меня) ответ сам по себе был довольно неточным. Поэтому я включил большинство предложений из комментариев после их тестирования. В частности, я убедился, что после выполнения этой процедуры репозиторий действительно уменьшился (чего не было для меня и других людей). Я надеюсь, что это нормально. - person Blaisorblade; 20.02.2012
comment
Чтобы удалить ссылки в Windows, вы можете использовать: for / f% a IN ('git for-each-ref --format =% (refname) refs / original /') DO git update-ref -d% a - person KindDragon; 06.04.2012
comment
Я обнаружил, что git-subtree тоже эффективен и намного проще. Найдено с помощью этого связанного вопроса / ответа: stackoverflow.com/a/1307969/72 - person pix0r; 18.04.2012
comment
В Windows я сначала попробовал выполнить действия из командной строки PowerShell, но шаг 5b завершился неудачно. Затем я перепробовал все шаги из git bash, и все сработало отлично. Спасибо! - person Ole Lynge; 07.07.2012
comment
Из 5 или около того похожих вопросов для меня это самый ценный ответ вместе с этой ссылкой: gbayer.com/development/ - person Vladimir Grigorov; 24.09.2012
comment
@Jarl предложил отредактировать и удалить параметр --aggressive для gc , поскольку он увеличивает размер репо: см. Также ссылка - person Kos; 29.11.2012
comment
Одно важное замечание: если у вас есть несколько веток, сохраняемых на шаге №2, вам придется выполнять шаги №4 и №5 через каждый из них. По крайней мере, это то, что случилось со мной. - person eddiemoya; 05.02.2013
comment
Разве это не создает ABC / вместо ABC / ABC /? - person Thorbjørn Ravn Andersen; 21.05.2013
comment
Итак, я, наверное, уже 10 раз использовал этот ответ и все еще хочу проголосовать за него снова. Тонны материала о stackoverflow были полезны, но этот ответ на данный момент является наиболее полезным. Я даже поместил его в evernote для сверхбыстрой ссылки. Спасибо! - person katyhuff; 13.06.2013
comment
Как это использовать, если вы хотите взять 2 справочника? - person ThanksBro; 18.06.2013
comment
N.B. это оставляет новое репо, структурированное как / ABC / ABC, а не просто / ABC (хотя кто-то отредактировал вопрос постфактум, чтобы сказать, что это было то, что было нужно) - person ysth; 11.12.2013
comment
@Paul - следует отметить, что этот подход не работает, если у папки ABC когда-либо было другое имя - в этом случае история останавливается в точке, когда она была переименована. - person Adam; 24.04.2014
comment
Это не освободило дисковое пространство для меня, в папке .git должна быть куча неиспользуемой истории, поскольку сокращенная папка составляет 10 КБ, а .git - 1,5 ГБ! Любые идеи? - person Toby; 30.04.2014
comment
После того, как я использовал filter-branch, все файлы в этом каталоге были удалены. - person orezvani; 26.05.2014
comment
Это действительно полезно. Однако, если / ABC раньше назывался / XY3 до тех пор, пока не будет 3 коммита перед HEAD, будет ли в результирующем репозитории ABC не только эти 3 коммита? По крайней мере, это то, что я нашел. - person qAp; 21.11.2014
comment
Эти ответы немного сбивают с толку ... Неужели пять шагов в этом ответе сейчас отсутствуют? Является ли ответ "Простой путь" под правильным ответом? Когда я впервые увидел этот ответ, я подумал, что ссылка «Отключить подкаталог в отдельный репозиторий Git» была на более подробную документацию и была резюмирована следующими пятью шагами. Можно это прояснить? Возможно, добавьте заголовок над пятью шагами, например, The Old Way (до версии 1.7.11). - person Sean; 22.01.2015
comment
Github wiki с теми же шагами help.github .com / article / - person VelLes; 28.09.2016
comment
Кроме того, вместо клонирования репо в первую очередь вы можете напрямую добавить подмодуль, который добавляет новое репо в git и также создает новое репо в вашем текущем репо! - person Shilpa; 30.04.2018
comment
github.com/newren/git-filter-repo - это способ, предлагаемый самим git сделать это легко. - person Juan Saravia; 28.10.2020

Легкий путь ™

Оказывается, это настолько распространенная и полезная практика, что повелители Git упростили ее, но у вас должна быть более новая версия Git (›= 1.7.11 мая 2012 г.). См. приложение, чтобы узнать, как установить последнюю версию Git. Кроме того, в пошаговом руководстве ниже есть реальный пример.

  1. Подготовить старое репо

     cd <big-repo>
     git subtree split -P <name-of-folder> -b <name-of-new-branch>
    

Примечание. <name-of-folder> НЕ должен содержать начальные или конечные символы. Например, папка с именем subproject ДОЛЖНА передаваться как subproject, НЕ ./subproject/.

Примечание для пользователей Windows: если глубина вашей папки составляет ›1, <name-of-folder> должен иметь разделитель папок в стиле * nix (/). Например, папка с именем path1\path2\subproject ДОЛЖНА быть передана как path1/path2/subproject

  1. Создать новое репо

     mkdir ~/<new-repo> && cd ~/<new-repo>
     git init
     git pull </path/to/big-repo> <name-of-new-branch>
    
  2. Свяжите новое репо с GitHub или где-нибудь еще

     git remote add origin <[email protected]:user/new-repo.git>
     git push -u origin master
    
  3. Очистка внутри <big-repo>, при желании

     git rm -rf <name-of-folder>
    

Примечание: все исторические ссылки останутся в хранилище. См. Приложение ниже, если вас действительно беспокоит фиксация пароля или вам нужно уменьшить размер файла вашей .git папки.


Прохождение

Это те же шаги, что и выше, но следуя моим точным шагам для моего репозитория вместо использования <meta-named-things>.

Вот проект, который у меня есть для реализации модулей браузера JavaScript в узле:

tree ~/node-browser-compat

node-browser-compat
├── ArrayBuffer
├── Audio
├── Blob
├── FormData
├── atob
├── btoa
├── location
└── navigator

Я хочу выделить одну папку btoa в отдельный репозиторий Git

cd ~/node-browser-compat/
git subtree split -P btoa -b btoa-only

Теперь у меня есть новая ветка btoa-only, в которой есть коммиты только для btoa, и я хочу создать новый репозиторий.

mkdir ~/btoa/ && cd ~/btoa/
git init
git pull ~/node-browser-compat btoa-only

Затем я создаю новое репо на GitHub или Bitbucket или на каком-либо другом и добавляю его как origin

git remote add origin [email protected]:node-browser-compat/btoa.git
git push -u origin master

Счастливый день!

Примечание. Если вы создали репо с README.md, .gitignore и LICENSE, вам нужно сначала извлечь:

git pull origin master
git push origin master

Наконец, я хочу удалить папку из большего репо.

git rm -rf btoa

Приложение

Последний Git для macOS

Чтобы получить последнюю версию Git с помощью Homebrew:

brew install git

Последний Git на Ubuntu

sudo apt-get update
sudo apt-get install git
git --version

Если это не сработает (у вас очень старая версия Ubuntu), попробуйте

sudo add-apt-repository ppa:git-core/ppa
sudo apt-get update
sudo apt-get install git

Если это все еще не работает, попробуйте

sudo chmod +x /usr/share/doc/git/contrib/subtree/git-subtree.sh
sudo ln -s \
/usr/share/doc/git/contrib/subtree/git-subtree.sh \
/usr/lib/git-core/git-subtree

Спасибо rui.araujo из комментариев.

Очистка вашей истории

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

git filter-branch --prune-empty --tree-filter 'rm -rf <name-of-folder>' HEAD

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

git log -- <name-of-folder> # should show nothing

Однако вы не можете отправлять удаление на GitHub и т.п. Если вы попытаетесь, вы получите сообщение об ошибке, и вам придется git pull, прежде чем вы сможете git push - и тогда вы снова получите все, что есть в вашей истории.

Поэтому, если вы хотите удалить историю из источника, то есть удалить ее из GitHub, Bitbucket и т. Д., Вам необходимо удалить репо и повторно отправить обрезанную копию репо. Но подождите - это еще не все! - если вы действительно беспокоитесь о том, чтобы избавиться от пароля или чего-то подобного, вам необходимо удалить резервную копию (см. ниже).

Делаем .git меньше

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

Поэтому, если вы действительно хотите очистить корзину, чтобы уменьшить размер клона репо, вам нужно сделать все эти действительно странные вещи:

rm -rf .git/refs/original/ && \
git reflog expire --all && \
git gc --aggressive --prune=now

git reflog expire --all --expire-unreachable=0
git repack -A -d
git prune

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

Кредит

person coolaj86    schedule 25.07.2013
comment
git subtree по-прежнему является частью папки contrib и не устанавливается по умолчанию во всех дистрибутивах. github.com/git/git/blob/master/contrib/subtree - person onionjake; 02.08.2013
comment
... в частности, не в Ubuntu, даже не при использовании ppa:git-core/ppa :-( - person krlmlr; 15.08.2013
comment
@krlmlr sudo chmod + x /usr/share/doc/git/contrib/subtree/git-subtree.sh sudo ln -s /usr/share/doc/git/contrib/subtree/git-subtree.sh / usr / lib / git-core / git-subtree Для активации в Ubuntu 13.04 - person rui.araujo; 26.08.2013
comment
Если вы отправили пароль в общедоступный репозиторий, вам следует изменить пароль, а не пытаться удалить его из общедоступного репозитория и надеяться, что его никто не видел. - person Miles Rout; 18.09.2013
comment
Вы можете переопределить ошибку push при отправке на GitHub, выполнив git push -f для принудительной перезаписи. - person erbridge; 18.10.2013
comment
@ rui.araujo ваш комментарий очень полезен и делает ответ полным. Было бы хорошо, если бы это было добавлено к собственно ответу. - person elaRosca; 07.11.2013
comment
У меня это не работает. Когда я звоню git pull </path/to/big-repo> <name-of-new-branch>, я получаю сообщение об ошибке: Couldn't find remote ref <name-of-new-branch>. - person Tola Odejayi; 16.12.2013
comment
Это действительно помогло мне с множеством репозиториев, особенно с использованием svn2git, а затем разделением репозитория на несколько репозиториев Git ... Посмотрите примеры на github.com/marcellodesales/svnedge-*. - person Marcello de Sales; 16.12.2013
comment
согласно kernel.org/pub/software/ scm / git / docs / git-filter-branch.html для git filter-branch, использование git rm вместо rm выполняется быстрее. - person Memming; 20.01.2014
comment
в ubuntu 13.10 вы можете найти его в указанном месте: /usr/share/doc/git/contrib/subtree/git-subtree.sh (без использования ppa). - person Christian Fritz; 19.03.2014
comment
При очистке не следует ли также удалить ветвь, созданную с помощью git subtree? - person rmoestl; 26.03.2014
comment
Что если у нас есть подкаталог, который ранее был переименован? Я пробовал это на <name-of-folder>, который в какой-то момент был переименован с <another-name-of-folder>. Когда я смотрю на историю в <new-repo>, первоначальная фиксация - это та, которая переименовала папку - вся история до этого стирается из <new-repo> - person Adam; 24.04.2014
comment
re последний git для OS X: нет необходимости возиться с brew или любым другим дополнительным менеджером пакетов, потому что инструменты командной строки XCode включают Git. Доступно от Apple бесплатно, установка / обновление без каких-либо проблем. - person Laryx Decidua; 03.10.2014
comment
похоже, создается новое репо с содержимым ABC/, но новое репо не содержит самой папки ABC/, как задан вопрос. Как бы ты это сделал? - person woojoo666; 08.10.2014
comment
В этом примере мне кажется, что это просто foo names. xyz, xyz1, xyz2 и т. д. В любом случае mkdir ABC/; git mv ./* ABC/. Я уверен, что есть способ дать префикс - может быть git read-tree --prefix=spoon-knife/ -u spoon-knife/master. См. help.github.com/articles/about-git-subtree-merges < / а>. - person coolaj86; 10.10.2014
comment
Чтобы перетащить разделенное поддерево в папку: git subtree add --prefix <new/folder/path> </path/to/big-repo> <name-of-new-branch> - person Bluu; 12.11.2014
comment
@Bluu Я пробовал это на пустом репо, и это не сработало. Но я добавил свою первую фиксацию, и после этого ваше решение сработало для меня. - person Umair A.; 08.02.2015
comment
Есть ли способ применить этот метод, чтобы разделить несколько подпапок в новый репозиторий git? - person Anthony O.; 15.06.2015
comment
То, что я наконец сделал, чтобы разделить несколько подпапок в новый проект, было в 1. до subtree split, переместил подпапки, которые я хотел, в уникальную подпапку, переписав историю, чтобы сохранить ее при вытягивании (шаг 2.): git filter-branch --tree-filter "mkdir <name-of-folder>; mv sub1 sub2 <name-of-folder>/" HEAD - person Anthony O.; 15.06.2015
comment
@AnthonyO. Не могли бы вы подробнее рассказать о своем методе? Или вообще написать ответ? Я застрял здесь и наверняка проголосую за это. - person Pierre Henry; 15.06.2015
comment
Наконец, я использовал решение, данное в stackoverflow.com/questions/19954485/ у меня пока работает хорошо. - person Pierre Henry; 15.06.2015
comment
Привет @AnthonyO. Я пытаюсь сделать что-то похожее на то, что вы написали, для перемещения нескольких подкаталогов, но для меня это не удается. Я получаю ошибку rename sub1 to folder/sub1: No such file or directory и то же самое для второго каталога. Я что-то упускаю ? - person Jason; 05.08.2015
comment
@ Джейсон и Пьер Генри: Я написал то, что сделал, отдельным ответом. Вот он: stackoverflow.com/a/31859803/535203 - person Anthony O.; 06.08.2015
comment
Привет @AnthonyO. Я также задавал здесь аналогичный вопрос: Разделение многих подкаталогов на новый , отдельный репозиторий Git и заставил его работать. Но спасибо, я тоже попробую ваш ответ. - person Jason; 06.08.2015
comment
Вероятно, неплохо передать --prune-empty в filter-branch, чтобы удалить пустые коммиты, возникающие из-за удаления каталога. - person JaviMerino; 12.08.2015
comment
Как следствие этого очень полезного ответа - если кто-то еще будет использовать подход поддерева, рекомендуется вручную разделить файл .gitattributes! Я стал жертвой этого и получил предупреждение CRLF / LF после добавления единственного символа "" в файл. - person cowboydan; 20.08.2015
comment
Насколько я понимаю, с помощью этого метода (git subtree) вы потеряете как ветки, так и теги. Используя git filter-branch-метод, как описано в stackoverflow.com/a/359759/300632, можно сохранить / переписать ветки и теги . - person Jarl; 14.10.2015
comment
Это решение не сохраняет историю. - person Cœur; 11.11.2015
comment
Если ваша цель - связать новый репозиторий как submodule исходного проекта, тогда вам нужен этот ответ (§) из Как извлечь подкаталог git и создать подмодуль этого?. - person KrisWebDev; 14.11.2015
comment
Если в какой-то момент в прошлом вы запускали git subtree, вы можете получить fatal: bad object ошибку при повторном запуске команды. Избегайте этого, передав параметр --ignore-joins. - person Joe Leo; 22.01.2016
comment
Этот метод оказывается практически бесполезным - если вы последуете ему, вы потеряете ВСЕ ветки, кроме master, и всю историю, которая может быть в этих ветвях. - person Chris Dodd; 07.04.2016
comment
См. Ответ rogerdpack, если вы когда-либо переименовывали / перемещали каталоги в своих коммитах, которые вы хотите сохранить. Методы filter-branch и subtree не сохраняют фиксации усечения при переименовании каталогов. - person Adam; 30.10.2017
comment
Требования к специальному пути к каталогу подчеркивают большую проблему, заключающуюся в том, что git иногда внутренне несовместим, что делает его гораздо более запутанным. - person Kelly Elton; 25.04.2018
comment
Команды popd и pushd делают это довольно неявным и затрудняют понимание того, что они намереваются делать ... - person jones77; 04.06.2018
comment
Команда git subtree split намного медленнее, чем подход filter-branch. - person Daniel Schilling; 03.12.2019
comment
В CentOS 7 выполните sudo yum install git-subtree, чтобы получить команду subtree. - person Daniel; 20.12.2019
comment
как насчет того, если у вашего исходного репозитория много веток? это решение просто сохраняет главную ветку - person YaP; 27.01.2020
comment
Используя подход git subtree, описанный здесь, есть ли способ сохранить существующие ветки (и теги)? Поскольку вся соответствующая история сохраняется, речь должна идти о восстановлении указателя на данный коммит? - person jmon12; 05.03.2020

Ответ Павла создает новый репозиторий, содержащий / ABC, но не удаляет / ABC из / XYZ. Следующая команда удалит / ABC из / XYZ:

git filter-branch --tree-filter "rm -rf ABC" --prune-empty HEAD

Конечно, сначала протестируйте его в репозитории clone --no-hardlinks, а затем выполните команды reset, gc и prune, перечисленные Полом.

person pgs    schedule 05.06.2009
comment
сделайте это git filter-branch --index-filter "git rm -r -f --cached --ignore-unmatch ABC" --prune-empty HEAD, и это будет намного быстрее. index-filter работает с индексом, в то время как древовидный фильтр должен проверять и обрабатывать все для каждой фиксации. - person fmarc; 17.09.2009
comment
в некоторых случаях испортить историю репозитория XYZ - это излишне ... просто rm -rf ABC; git rm -r ABC; git commit -m «извлечение ABC в собственное репо» подойдет для большинства людей лучше. - person Evgeny; 29.10.2010
comment
Вы, вероятно, захотите использовать -f (force) в этой команде, если вы делаете это более одного раза, например, чтобы удалить два каталога после того, как они были разделены. В противном случае вы получите сообщение «Невозможно создать новую резервную копию». - person Brian Carlton; 18.04.2011
comment
Если вы используете метод --index-filter, вы также можете сделать это git rm -q -r -f, чтобы каждый вызов не печатал строку для каждого удаляемого файла. - person Eric Naeseth; 12.10.2011
comment
Я бы посоветовал отредактировать ответ Пола только потому, что он настолько подробен. - person Erik Aronesty; 05.03.2014

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

  1. Сделайте клон и фильтр:

    git clone --no-hardlinks foo bar; cd bar
    git filter-branch --subdirectory-filter subdir/you/want
    
  2. Удалите все ссылки на старую историю. «Origin» отслеживал ваш клон, а «original» - это то место, где filter-branch сохраняет старый материал:

    git remote rm origin
    git update-ref -d refs/original/refs/heads/master
    git reflog expire --expire=now --all
    
  3. Даже сейчас ваша история может застрять в пакете, который fsck не коснется. Разорвите его в клочья, создав новый pack-файл и удалив неиспользуемые объекты:

    git repack -ad
    

Есть объяснение этого в руководство для ветки фильтра.

person Josh Lee    schedule 19.10.2009
comment
Я думаю, что-то вроде git gc --aggressive --prune=now все еще отсутствует, не так ли? - person Albert; 12.07.2012
comment
@Albert Команда repack позаботится об этом, и не будет никаких незакрепленных объектов. - person Josh Lee; 12.07.2012
comment
да, git gc --aggressive --prune=now сократили большую часть нового репо - person Tomek Wyderka; 02.04.2013
comment
Просто и элегантно. Спасибо! - person Marco Pelegrini; 29.08.2016
comment
И после всего этого я все еще получаю ту же ошибку, что и раньше. фатальный: упакованный объект xxxxxx (хранящийся в .git / objects / pack / pack-yyyyyyyy.pack) поврежден - person AaA; 02.09.2020

Изменить: добавлен сценарий Bash.

Приведенные здесь ответы только частично работали для меня; В кеше осталось много больших файлов. Что наконец сработало (после нескольких часов работы в #git на freenode):

git clone --no-hardlinks file:///SOURCE /tmp/blubb
cd blubb
git filter-branch --subdirectory-filter ./PATH_TO_EXTRACT  --prune-empty --tag-name-filter cat -- --all
git clone file:///tmp/blubb/ /tmp/blooh
cd /tmp/blooh
git reflog expire --expire=now --all
git repack -ad
git gc --prune=now

В предыдущих решениях размер репозитория составлял около 100 МБ. Этот уменьшил его до 1,7 МБ. Может кому поможет :)


Следующий сценарий bash автоматизирует задачу:

!/bin/bash

if (( $# < 3 ))
then
    echo "Usage:   $0 </path/to/repo/> <directory/to/extract/> <newName>"
    echo
    echo "Example: $0 /Projects/42.git first/answer/ firstAnswer"
    exit 1
fi


clone=/tmp/${3}Clone
newN=/tmp/${3}

git clone --no-hardlinks file://$1 ${clone}
cd ${clone}

git filter-branch --subdirectory-filter $2  --prune-empty --tag-name-filter cat -- --all

git clone file://${clone} ${newN}
cd ${newN}

git reflog expire --expire=now --all
git repack -ad
git gc --prune=now
person Simon A. Eugster    schedule 09.06.2011

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

git filter-branch --prune-empty --subdirectory-filter <YOUR_SUBDIR_TO_KEEP> master
git push <MY_NEW_REMOTE_URL> -f .
person jeremyjjbrown    schedule 20.08.2014
comment
Это сработало как шарм. YOUR_SUBDIR в приведенном выше примере - это подкаталог, который вы хотите СОХРАНИТЬ, все остальное будет удалено. - person J.T. Taylor; 19.04.2015
comment
Обновления на основе вашего комментария. - person jeremyjjbrown; 20.04.2015
comment
Это не отвечает на вопрос. В документации указано The result will contain that directory (and only that) as its project root., и вы действительно получите именно это, т.е. исходная структура проекта не сохраняется. - person NicBright; 02.06.2017
comment
@NicBright Можете ли вы проиллюстрировать вашу проблему с XYZ и ABC, как в вопросе, чтобы показать, что не так? - person Adam; 26.10.2017
comment
@jeremyjjbrown можно ли повторно использовать клонированное репо и не использовать новое репо, т.е. мой вопрос здесь stackoverflow.com/questions/49269602/ - person Qiulang; 03.04.2018

При запуске git filter-branch с использованием более новой версии git (2.22+ может быть?) Он говорит об использовании этого нового инструмента git -фильтр-репо. Этот инструмент, безусловно, упростил мне задачу.

Фильтрация с помощью filter-repo

Команды для создания репозитория XYZ из исходного вопроса:

# create local clone of original repo in directory XYZ
tmp $ git clone [email protected]:user/original.git XYZ

# switch to working in XYZ
tmp $ cd XYZ

# keep subdirectories XY1 and XY2 (dropping ABC)
XYZ $ git filter-repo --path XY1 --path XY2

# note: original remote origin was dropped
# (protecting against accidental pushes overwriting original repo data)

# XYZ $ ls -1
# XY1
# XY2

# XYZ $ git log --oneline
# last commit modifying ./XY1 or ./XY2
# first commit modifying ./XY1 or ./XY2

# point at new hosted, dedicated repo
XYZ $ git remote add origin [email protected]:user/XYZ.git

# push (and track) remote master
XYZ $ git push -u origin master

предположения: * удаленное репо XYZ было новым и пустым перед отправкой

Фильтрация и перемещение

В моем случае я также хотел переместить пару каталогов для более согласованной структуры. Сначала я выполнил эту простую команду filter-repo, а затем git mv dir-to-rename, но обнаружил, что могу получить немного «лучшую» историю, используя параметр --path-rename. Вместо того, чтобы видеть последний измененный 5 hours ago в перемещенных файлах в новом репо, я теперь вижу last year (в пользовательском интерфейсе GitHub), который соответствует измененному времени в исходном репо.

Вместо того...

git filter-repo --path XY1 --path XY2 --path inconsistent
git mv inconsistent XY3  # which updates last modification time

В конце концов я сбежал ...

git filter-repo --path XY1 --path XY2 --path inconsistent --path-rename inconsistent:XY3
Notes:
  • Я думал, что Сообщение в блоге Git Rev News хорошо объясняет причину создания еще одного инструмента фильтрации репо.
  • Сначала я попробовал создать подкаталог, соответствующий имени целевого репозитория в исходном репозитории, и выполнить затем фильтрацию (с использованием git filter-repo --subdirectory-filter dir-matching-new-repo-name). Эта команда правильно преобразовала этот подкаталог в корень скопированного локального репо, но она также привела к истории только трех коммитов, которые потребовались для создания подкаталога. (Я не понимал, что --path можно указывать несколько раз; таким образом, устраняется необходимость в создании подкаталога в исходном репо.) Поскольку кто-то привязался к исходному репо к тому времени, когда я заметил, что мне не удалось продолжить истории, я просто использовал git reset commit-before-subdir-move --hard после команды clone и добавил --force к команде filter-repo, чтобы заставить ее работать с слегка измененным локальным клоном.
git clone ...
git reset HEAD~7 --hard      # roll back before mistake
git filter-repo ... --force  # tell filter-repo the alterations are expected
  • Я был в тупике при установке, так как не знал о шаблоне расширения с git, но в конечном итоге я клонировал git- filter-repo и привязал его к $(git --exec-path):
ln -s ~/github/newren/git-filter-repo/git-filter-repo $(git --exec-path)
person lpearson    schedule 21.11.2019
comment
Проголосовали за рекомендацию нового инструмента filter-repo (который я представил в прошлом месяце в stackoverflow.com/a/58251653/6309) - person VonC; 22.11.2019
comment
Использование git-filter-repo определенно должно быть предпочтительным подходом на данном этапе. Это намного, намного быстрее и безопаснее, чем git-filter-branch, и защищает от множества ошибок, с которыми можно столкнуться при переписывании истории git. Надеюсь, этот ответ привлечет больше внимания, поскольку он предназначен для git-filter-repo. - person Jeremy Caney; 28.12.2019
comment
Фактически я в настоящее время пытаюсь заставить все работать с git filter-repo, но, к сожалению, после его запуска мне не хватает файлов, которые были добавлены в фиксацию, содержащие путь, который был удален с помощью filter-repo. Например: Foo/ Foo.cs Bar/ Bar.cs Все были добавлены в один коммит. Я хочу переместить Foo и Bar в отдельные репозитории. Поэтому я клонировал свое репо в папку, соответствующую новому имени репо, и удалил git filter-repo -path Foo Foo. Я говорю о гораздо более крупном репо, и он работает для всех остальных файлов, но не для такого созвездия. - person bego; 26.04.2021
comment
Если файлы были ранее перемещены / переименованы, это не будет автоматически сохранять историю перед перемещением / переименованием. Однако, если вы включите в команду исходные пути / имена файлов, эта история не будет удалена. Например, git filter-repo --path CurrentPathAfterRename --path OldPathBeforeRename. git filter-repo --analyze создает файл renames.txt, который может помочь в их определении. Кроме того, вы можете найти подобный сценарий полезным. - person Joel Leach; 11.07.2021

Обновление: модуль git-subtree оказался настолько полезным, что команда git вытащила его в ядро ​​и сделала git subtree. См. Здесь: Отключить (переместить) подкаталог в отдельный репозиторий Git

git-subtree может быть полезен для этого

http://github.com/apenwarr/git-subtree/blob/master/git-subtree.txt (не рекомендуется)

http://psionides.jogger.pl/2010/02/04/sharing-code-between-projects-with-git-subtree/

person D W    schedule 22.03.2010
comment
git-subtree теперь является частью Git, хотя и находится в дереве contrib, поэтому не всегда устанавливается по умолчанию. Я знаю, что он устанавливается по формуле Homebrew git, но без его справочной страницы. Таким образом, апенварр называет свою версию устаревшей. - person echristopherson; 10.05.2013

Вот небольшая модификация CoolAJ86 ответ" Простой способ ", чтобы разделить несколько подпапок (скажем, sub1 и sub2) в новый репозиторий git.

Простой способ (несколько подпапок)

  1. Подготовить старое репо

    pushd <big-repo>
    git filter-branch --tree-filter "mkdir <name-of-folder>; mv <sub1> <sub2> <name-of-folder>/" HEAD
    git subtree split -P <name-of-folder> -b <name-of-new-branch>
    popd
    

    Примечание. <name-of-folder> НЕ должен содержать начальные или конечные символы. Например, папка с именем subproject ДОЛЖНА передаваться как subproject, НЕ ./subproject/.

    Примечание для пользователей Windows: если глубина вашей папки> 1, <name-of-folder> должен иметь разделитель папок в стиле * nix (/). Например, папка с именем path1\path2\subproject ДОЛЖНА быть передана как path1/path2/subproject. Более того, используйте не mvcommand, а move.

    Последнее замечание: уникальное и большое отличие от основного ответа - это вторая строка скрипта "git filter-branch..."

  2. Создать новое репо

    mkdir <new-repo>
    pushd <new-repo>
    
    git init
    git pull </path/to/big-repo> <name-of-new-branch>
    
  3. Свяжите новое репо с Github или где-нибудь еще

    git remote add origin <[email protected]:my-user/new-repo.git>
    git push origin -u master
    
  4. Очистка, при желании

    popd # get out of <new-repo>
    pushd <big-repo>
    
    git rm -rf <name-of-folder>
    

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

person Anthony O.    schedule 06.08.2015
comment
Это сработало для меня с небольшими изменениями. Поскольку мои папки sub1 и sub2 не существовали в начальной версии, мне пришлось изменить свой --tree-filter скрипт следующим образом: "mkdir <name-of-folder>; if [ -d sub1 ]; then mv <sub1> <name-of-folder>/; fi". Для второй команды filter-branch я заменил ‹sub1› на ‹sub2›, пропустил создание ‹name-of-folder› и включил -f после filter-branch, чтобы переопределить предупреждение о существующей резервной копии. - person pglezen; 11.02.2016
comment
Это не работает, если какой-либо из подкаталогов изменился во время истории в git. Как это можно решить? - person nietras; 03.03.2016
comment
@nietras см. ответ rogerdpack. Мне потребовалось время, чтобы найти его после прочтения и усвоения всей информации в этих других ответах. - person Adam; 30.10.2017

Исходный вопрос требует, чтобы XYZ / ABC / (* файлы) превратились в ABC / ABC / (* файлы). После реализации принятого ответа для моего собственного кода я заметил, что он фактически изменяет XYZ / ABC / (* файлы) на ABC / (* файлы). На странице руководства filter-branch даже сказано:

Результат будет содержать этот каталог (и только он) в качестве корня проекта. "

Другими словами, он продвигает папку верхнего уровня «на один уровень выше». Это важное различие, потому что, например, в своей истории я переименовал папку верхнего уровня. Перемещая папки «на один уровень выше», git теряет непрерывность при фиксации, в которой я переименовал.

Я потерял непрерывность после перехода-фильтра

Тогда мой ответ на вопрос - сделать 2 копии репозитория и вручную удалить папки, которые вы хотите сохранить в каждой. Страница руководства поддерживает меня следующим образом:

[...] избегайте использования [этой команды], если для решения вашей проблемы будет достаточно простой одиночной фиксации.

person MM.    schedule 17.04.2012
comment
Мне нравится стиль этого графика. Могу я спросить, какой инструмент вы используете? - person Slipp D. Thompson; 30.03.2013
comment
Tower для Mac. Мне это и вправду нравится. Само по себе почти стоит переключиться на Mac. - person MM.; 03.04.2013
comment
Да, хотя в моем случае моя вложенная папка targetdir была переименована в какой-то момент, и git filter-branch просто завершил ее, удалив все коммиты, сделанные до переименования! Шокирует, учитывая, насколько искусен Git в отслеживании таких вещей и даже в переносе отдельных фрагментов контента! - person Jay Allen; 31.05.2013
comment
Ах да, если кто-то окажется в той же лодке, вот команду, которую я использовал. Не забывайте, что git rm принимает несколько аргументов, поэтому нет причин запускать его для каждого файла / папки: BYEBYE="dir/subdir2 dir2 file1 dir/file2"; git filter-branch -f --index-filter "git rm -q -r -f --cached --ignore-unmatch $BYEBYE" --prune-empty -- --all - person Jay Allen; 31.05.2013

Чтобы добавить в ответ Пола, я обнаружил, что для окончательного восстановления места мне нужно отправить HEAD в чистый репозиторий, и это обрезает уменьшить размер каталога .git / objects / pack.

i.e.

$ mkdir ...ABC.git
$ cd ...ABC.git
$ git init --bare

После обрезки gc также выполните:

$ git push ...ABC.git HEAD

Тогда ты можешь сделать

$ git clone ...ABC.git

и размер ABC / .git уменьшен

На самом деле, некоторые из трудоемких шагов (например, git gc) не требуются при нажатии на очистку репозитория, а именно:

$ git clone --no-hardlinks /XYZ /ABC
$ git filter-branch --subdirectory-filter ABC HEAD
$ git reset --hard
$ git push ...ABC.git HEAD
person Case Larsen    schedule 25.07.2009

Правильный путь сейчас следующий:

git filter-branch --prune-empty --subdirectory-filter FOLDER_NAME [first_branch] [another_branch]

На GitHub теперь есть даже небольшая статья о таких случаях.

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

Итак, ваш алгоритм должен быть:

  1. клонировать удаленное репо в другой каталог
  2. используя git filter-branch оставил только файлы в каком-то подкаталоге, нажмите на новый удаленный
  3. создать фиксацию, чтобы удалить этот подкаталог из исходного удаленного репо
person Oleksandr Shapovalov    schedule 12.11.2014

Я рекомендую руководство GitHub по разделению подпапок на новые репозиторий. Шаги аналогичны ответу Пола, но мне показалось, что их инструкции легче понять.

Я изменил инструкции, чтобы они применялись для локального репозитория, а не для репозитория, размещенного на GitHub.


Разделение подпапки в новый репозиторий

  1. Откройте Git Bash.

  2. Измените текущий рабочий каталог на место, где вы хотите создать новый репозиторий.

  3. Клонируйте репозиторий, содержащий вложенную папку.

git clone OLD-REPOSITORY-FOLDER NEW-REPOSITORY-FOLDER
  1. Измените текущий рабочий каталог на свой клонированный репозиторий.

cd REPOSITORY-NAME
  1. To filter out the subfolder from the rest of the files in the repository, run git filter-branch, supplying this information:
    • FOLDER-NAME: The folder within your project that you'd like to create a separate repository from.
      • Tip: Windows users should use / to delimit folders.
    • BRANCH-NAME: ветвь по умолчанию для вашего текущего проекта, например master или gh-pages.

git filter-branch --prune-empty --subdirectory-filter FOLDER-NAME  BRANCH-NAME 
# Filter the specified branch in your directory and remove empty commits
Rewrite 48dc599c80e20527ed902928085e7861e6b3cbe6 (89/89)
Ref 'refs/heads/BRANCH-NAME' was rewritten
person Stevoisiak    schedule 31.08.2017
comment
Хороший пост, но я заметил, что в первом абзаце документа, который вы связали, говорится: If you create a new clone of the repository, you won't lose any of your Git history or changes when you split a folder into a separate repository. Однако, согласно комментариям ко всем ответам здесь, как filter-branch, так и сценарий subtree приводят к потере истории, когда подкаталог был переименован. Что можно сделать для решения этой проблемы? - person Adam; 30.10.2017
comment
Нашел решение для сохранения всех коммитов, включая предыдущие переименования / перемещения каталогов - это ответ rogerdpack именно на этот вопрос. - person Adam; 30.10.2017
comment
Единственная проблема в том, что я больше не могу использовать клонированное репо. - person Qiulang; 03.04.2018

Похоже, что большинство (все?) Ответов здесь полагаются на ту или иную форму git filter-branch --subdirectory-filter и ему подобных. Однако это может сработать в большинстве случаев в некоторых случаях, например, когда вы переименовали папку, например:

 ABC/
    /move_this_dir # did some work here, then renamed it to

ABC/
    /move_this_dir_renamed

Если вы выполните обычный стиль фильтра git для извлечения move_this_dir_renamed, вы потеряете историю изменений файлов, которые произошли с того момента, когда он изначально был move_this_dir (ref).

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

  1. Клонировать многомодульный проект локально
  2. Филиалы - проверьте, что там: git branch -a
  3. Выполните проверку каждой ветки, которая будет включена в разделение, чтобы получить локальную копию на вашей рабочей станции: git checkout --track origin/branchABC
  4. Сделайте копию в новом каталоге: cp -r oldmultimod simple
  5. Перейдите в новую копию проекта: cd simple
  6. Избавьтесь от других модулей, которые не нужны в этом проекте:
  7. git rm otherModule1 other2 other3
  8. Теперь остается только subdir целевого модуля
  9. Избавьтесь от подкаталога модуля, чтобы корень модуля стал корнем нового проекта.
  10. git mv moduleSubdir1/* .
  11. Удалить подкаталог реликвии: rmdir moduleSubdir1
  12. Проверить изменения в любой момент: git status
  13. Создайте новое репозиторий git и скопируйте его URL-адрес, чтобы указать в нем этот проект:
  14. git remote set-url origin http://mygithost:8080/git/our-splitted-module-repo
  15. Убедитесь, что все в порядке: git remote -v
  16. Отправьте изменения в удаленное репо: git push
  17. Зайдите в удаленное репо и проверьте все, что есть
  18. Повторите это для любой другой необходимой ветки: git checkout branch2

Это следует за документом github. Разделение подпапки в новый репозиторий, шаги 6–11, чтобы переместить модуль в новое репозиторий.

Это не сэкономит вам места в папке .git, но сохранит всю историю изменений этих файлов даже при переименовании. И это может не стоить того, если не так много потеряно истории и т. Д. Но, по крайней мере, вы гарантированно не потеряете старые коммиты!

person rogerdpack    schedule 19.09.2016
comment
Нашел иголку в мерзком стоге сена! Теперь я могу хранить ВСЕ историю своих коммитов. - person Adam; 30.10.2017

У меня была именно эта проблема, но все стандартные решения, основанные на git filter-branch, работали очень медленно. Если у вас небольшой репозиторий, это может не быть проблемой, это было для меня. Я написал еще одну программу фильтрации git на основе libgit2, которая в качестве первого шага создает ветки для каждой фильтрации основного репозитория, а затем подталкивает их к очистке репозиториев в качестве следующего шага. В моем репозитории (500 МБ 100000 коммитов) стандартные методы git filter-branch заняли несколько дней. Моей программе требуется несколько минут, чтобы выполнить такую ​​же фильтрацию.

У него сказочное имя git_filter, и он живет здесь:

https://github.com/slobobaby/git_filter

на GitHub.

Надеюсь, это кому-то пригодится.

person slobobaby    schedule 10.03.2014

Используйте эту команду фильтра, чтобы удалить подкаталог, сохранив теги и ветки:

git filter-branch --index-filter \
"git rm -r -f --cached --ignore-unmatch DIR" --prune-empty \
--tag-name-filter cat -- --all
person cmcginty    schedule 28.10.2010
comment
что здесь за кот? - person rogerdpack; 19.09.2016

Как бы то ни было, вот как использовать GitHub на машине с Windows. Допустим, у вас есть клонированное репо, проживающее в C:\dir1. Структура каталогов выглядит так: C:\dir1\dir2\dir3. Каталог dir3 - это тот каталог, который я хочу сделать новым отдельным репо.

Github:

  1. Создайте новый репозиторий: MyTeam/mynewrepo

Подсказка Bash:

  1. $ cd c:/Dir1
  2. $ git filter-branch --prune-empty --subdirectory-filter dir2/dir3 HEAD
    Возвращено: Ref 'refs/heads/master' was rewritten (к сведению: dir2 / dir3 чувствительны к регистру.)

  3. $ git remote add some_name [email protected]:MyTeam/mynewrepo.git
    git remote add origin etc. не получилось, вернул "remote origin already exists"

  4. $ git push --progress some_name master

person James Lawruk    schedule 07.02.2012

Поскольку я упомянул выше, мне пришлось использовать обратное решение (удаление все коммиты не касаются моего dir/subdir/targetdir), который, казалось, работал довольно хорошо, удаляя около 95% коммитов (по желанию). Однако остаются две небольшие проблемы.

ПЕРВЫЙ, filter-branch проделал огромную работу по удалению коммитов, которые вводят или изменяют код, но, очевидно, коммиты слияния находятся ниже его позиции в Gitiverse.

Это косметическая проблема, с которой я, вероятно, смогу смириться (он говорит ... медленно отступая, отведя глаза).

ВТОРОЙ несколько оставшихся коммитов в значительной степени ВСЕ дублируются! Кажется, я приобрел вторую, повторяющуюся временную шкалу, которая охватывает практически всю историю проекта. Интересная вещь (которую вы можете видеть на картинке ниже) заключается в том, что мои три локальные ветки не все находятся на одной временной шкале (именно поэтому она существует, а не просто сборщик мусора).

Единственное, что я могу себе представить, это то, что один из удаленных коммитов был, возможно, единственным коммитом слияния, который filter-branch на самом деле удалил, и который создал параллельную временную шкалу, поскольку каждая теперь несвязанная цепочка брала свою собственную копию коммитов. (пожать плечами Где мой TARDiS?) Я почти уверен, что смогу исправить эту проблему, хотя мне очень хотелось бы понять, как это произошло.

В случае сумасшедшего mergefest-O-RAMA, я, вероятно, оставлю его в покое, поскольку он так прочно закрепился в моей истории коммитов - угрожая мне всякий раз, когда я подхожу -, на самом деле, похоже, он не вызывает любые не косметические проблемы и потому что в Tower.app он довольно хорош.

person Jay Allen    schedule 31.05.2013

Более легкий путь

  1. установите git splits. Я создал его как расширение git на основе решения jkeating.
  2. Разделите каталоги на локальную ветку #change into your repo's directory cd /path/to/repo #checkout the branch git checkout XYZ
    #split multiple directories into new branch XYZ git splits -b XYZ XY1 XY2

  3. Создайте где-нибудь пустое репо. Предположим, мы создали пустое репо с именем xyz на GitHub, у которого есть путь: [email protected]:simpliwp/xyz.git

  4. Нажать на новое репо. #add a new remote origin for the empty repo so we can push to the empty repo on GitHub git remote add origin_xyz [email protected]:simpliwp/xyz.git #push the branch to the empty repo's master branch git push origin_xyz XYZ:master

  5. Клонировать только что созданное удаленное репо в новый локальный каталог
    #change current directory out of the old repo cd /path/to/where/you/want/the/new/local/repo #clone the remote repo you just pushed to git clone [email protected]:simpliwp/xyz.git

person AndrewD    schedule 11.02.2015
comment
Преимущество этого метода по сравнению с Easy Way заключается в том, что пульт уже настроен для нового репо, поэтому вы можете сразу же добавить поддерево. На самом деле мне этот способ кажется проще (даже без git splits) - person M.M; 12.05.2015
comment
Благодарим AndrewD за публикацию этого решения. Я разветвил его репо, чтобы он работал на OSX (github.com/ricardoespsanto/git-splits) если это кому-нибудь пригодится - person ricardoespsanto; 09.02.2018

Вам может понадобиться что-то вроде «git reflog expire --expire = now --all» перед сборкой мусора, чтобы фактически очистить файлы. git filter-branch просто удаляет ссылки в истории, но не удаляет записи журнала ссылок, содержащие данные. Конечно, сначала проверьте это.

При этом использование моего диска резко упало, хотя мои первоначальные условия были несколько иными. Возможно --subdirectory-filter отменяет эту необходимость, но я в этом сомневаюсь.

person Community    schedule 12.06.2009

Ознакомьтесь с проектом git_split по адресу https://github.com/vangorra/git_split.

Превратите каталоги git в их собственные репозитории в их собственном местоположении. Никакого поддерева забавного дела. Этот скрипт возьмет существующий каталог в вашем репозитории git и превратит этот каталог в собственный независимый репозиторий. Попутно он скопирует всю историю изменений для указанного вами каталога.

./git_split.sh <src_repo> <src_branch> <relative_dir_path> <dest_repo>
        src_repo  - The source repo to pull from.
        src_branch - The branch of the source repo to pull from. (usually master)
        relative_dir_path   - Relative path of the directory in the source repo to split.
        dest_repo - The repo to push to.
person vangorra    schedule 06.01.2016

Поместите это в свой gitconfig:

reduce-to-subfolder = !sh -c 'git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter cookbooks/unicorn HEAD && git reset --hard && git for-each-ref refs/original/ | cut -f 2 | xargs -n 1 git update-ref -d && git reflog expire --expire=now --all && git gc --aggressive --prune=now && git remote rm origin'
person grosser    schedule 29.03.2013

Я уверен, что с git subtree все в порядке, но все мои подкаталоги управляемого кода git, который я хотел переместить, были в eclipse. Так что, если вы используете egit, это очень просто. Возьмите проект, который вы хотите переместить, и команда-> отключите его, а затем команда-> поделитесь им в новом месте. По умолчанию он будет пытаться использовать старое местоположение репо, но вы можете снять флажок использовать существующий выбор и выбрать новое место, чтобы переместить его. Всем привет, егит.

person stu    schedule 10.02.2016
comment
Прекрасная и замечательная часть поддерева - это то, что история вашего подкаталога попадает в ход. Если вам не нужна история, тогда ваш болезненно простой метод - это то, что вам нужно. - person pglezen; 11.02.2016

Вы можете легко попробовать https://help.github.com/enterprise/2.15/user/articles/splitting-a-subfolder-out-into-a-new-repository/

Это сработало для меня. Проблемы, с которыми я столкнулся в приведенных выше шагах, следующие:

  1. в этой команде git filter-branch --prune-empty --subdirectory-filter FOLDER-NAME BRANCH-NAME BRANCH-NAME - главный

  2. если последний шаг завершился неудачно при фиксации из-за проблемы с защитой, следуйте - https://docs.gitlab.com/ee/user/project/protected_branches.html.

person Barath Ravichander    schedule 10.01.2019

Я нашел довольно простое решение. Идея состоит в том, чтобы скопировать репозиторий, а затем просто удалить ненужную часть. Вот как это работает:

1) Клонируйте репозиторий, который хотите разделить

git clone [email protected]:testrepo/test.git

2) Перейти в папку git

cd test/

2) Удалите ненужные папки и зафиксируйте их

rm -r ABC/
git add .
enter code here
git commit -m 'Remove ABC'

3) Удалите ненужную историю форм папок с помощью BFG.

cd ..
java -jar bfg.jar --delete-folders "{ABC}" test
cd test/
git reflog expire --expire=now --all && git gc --prune=now --aggressive

для нескольких папок вы можете использовать запятую

java -jar bfg.jar --delete-folders "{ABC1,ABC2}" metric.git

4) Убедитесь, что в истории нет файлов / папок, которые вы только что удалили.

git log --diff-filter=D --summary | grep delete

5) Теперь у вас есть чистый репозиторий без ABC, поэтому просто вставьте его в новый источник.

remote add origin [email protected]:username/new_repo
git push -u origin master

Вот и все. Вы можете повторить шаги, чтобы получить другой репозиторий,

просто удалите XY1, XY2 и переименуйте XYZ -> ABC на шаге 3

person Vlad Troyan    schedule 28.03.2019
comment
Почти идеально ... но вы забыли git filter-branch --prune-empty, чтобы удалить все старые коммиты, которые теперь пусты. Сделать до того, как нажать на origin master! - person ZettaCircl; 24.05.2019
comment
Если вы допустили ошибку и по-прежнему хотите повторить попытку после удаления старой пустой фиксации, выполните: git push -u origin master --force-with-lease - person ZettaCircl; 24.05.2019

Эта замечательная статья, Исходная ссылка, проста для понимания. Документирование здесь на случай, если он станет недоступен.

1. Подготовка текущего репозитория

$ cd path/to/repository
$ git subtree split -P my-folder -b my-folder
Created branch 'my-folder'
aecbdc3c8fe2932529658f5ed40d95c135352eff

Имя папки должно быть относительным путем, начиная с корня репозитория.

2. Создание нового репозитория

$ cd my-folder
$ git init
Initialized empty Git repository in /Users/adamwest/Projects/learngit/shop/my-folder/.git/
$ git add .
$ git commit -m "initial commit"
[master (root-commit) 192c10b] initial commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 file

Здесь нам просто нужно записать cd в новую папку, инициализировать новый репозиторий и зафиксировать любое содержимое.

3. Добавьте новый удаленный репозиторий и нажмите

$ git remote add origin [email protected]:robertlyall/my-folder.git
$ git push origin -u master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 199 bytes | 199.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:robertlyall/my-folder.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

Мы добавляем сюда новый репозиторий, удаленный от GitHub, а затем помещаем в него нашу первую фиксацию.

4. Удалите папку из основного репозитория и нажмите

$ cd ../
$ git rm -rf my-folder
rm 'my-folder/file'
$ git commit -m "Remove old folder"
[master 56aedbe] remove old folder
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 my-folder/file
$ git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 217 bytes | 217.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To github.com:robertlyall/shop.git
   74dd8b3..56aedbe  master -> master

Наконец, мы возвращаемся в корневой каталог, удаляем папку из нашего основного репозитория, затем фиксируем и отправляем изменение. Теперь у нас есть папка в нашем основном репозитории, но она связана с совершенно отдельным репозиторием, который можно повторно использовать в нескольких проектах.

person infiniteLearner    schedule 10.07.2021