Отсоединить (переместить) подкаталог в отдельный Git-репозиторий
У меня есть Git- репозиторий, который содержит несколько подкаталогов. Теперь я обнаружил, что одно из подкаталогов не связано с другим и должно быть отсоединено от отдельного хранилища.
Как я могу сделать это, сохраняя историю файлов в подкаталоге?
Я думаю, я мог бы сделать клон и удалить ненужные части каждого клона, но я полагаю, что это даст мне полное дерево при проверке более ранней ревизии и т. Д. Это может быть приемлемо, но я предпочел бы сделать вид, что два репозитория не имеют общей истории.
Просто чтобы прояснить, у меня есть следующая структура:
XYZ/
.git/
XY1/
ABC/
XY2/
Но я бы хотел этого:
XYZ/
.git/
XY1/
XY2/
ABC/
.git/
ABC/
26 ответов
Обновление: этот процесс настолько распространен, что команда git значительно упростила его с помощью нового инструмента, git subtree
, Смотрите здесь: Отсоединить (переместить) подкаталог в отдельный Git-репозиторий
Вы хотите клонировать свой репозиторий, а затем использовать git filter-branch
чтобы пометить все, кроме подкаталога, который вы хотите в вашем новом репо для сбора мусора.
Чтобы клонировать ваш локальный репозиторий:
git clone /XYZ /ABC
(Примечание: репозиторий будет клонирован с использованием жестких ссылок, но это не проблема, поскольку файлы с жесткими ссылками не будут изменены сами по себе - будут созданы новые.)
Теперь давайте сохраним интересные ветки, которые мы хотим переписать, а затем удалим источник, чтобы избежать его появления и убедиться, что источник не будет ссылаться на старые коммиты:
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
Теперь вы можете также удалить теги, которые не имеют отношения к подпроекту; Вы также можете сделать это позже, но вам может понадобиться снова обрезать репо. Я не сделал этого и получил
WARNING: Ref 'refs/tags/v0.1' is unchanged
для всех тегов (поскольку все они не связаны с подпроектом); Кроме того, после удаления таких тегов будет освобождено больше места. По-видимомуgit filter-branch
должен быть в состоянии переписать другие теги, но я не мог проверить это. Если вы хотите удалить все теги, используйтеgit tag -l | xargs git tag -d
,Затем используйте 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
Затем удалите резервные копии, чтобы освободить место (хотя теперь операция разрушительна)
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
, Да, это действительно --космос-- all
, Это должны быть последние параметры для команды. Как обнаружил Матли, это сохраняет ветки проекта и теги, включенные в новый репозиторий.
Изменить: различные предложения из комментариев ниже были включены, чтобы убедиться, например, в том, что хранилище действительно сокращено (что не всегда было так раньше).
Легкий путь ™
Оказывается, это настолько распространенная и полезная практика, что повелители git сделали это действительно легко, но вам нужно иметь более новую версию git (>= 1.7.11, май 2012). Смотрите приложение, чтобы узнать, как установить последнюю версию git. Кроме того, есть реальный пример в пошаговом руководстве ниже.
Подготовьте старый репо
pushd <big-repo> 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
Создать новый репо
mkdir <new-repo> pushd <new-repo> git init git pull </path/to/big-repo> <name-of-new-branch>
Свяжите новый репо с Github или где угодно
git remote add origin <git@github.com:my-user/new-repo.git> git push origin -u master
Очистка, если желательно
popd # get out of <new-repo> pushd <big-repo> git rm -rf <name-of-folder>
Примечание. При этом все исторические ссылки останутся в хранилище. См. Приложение ниже, если вы действительно хотите ввести пароль или вам нужно уменьшить размер файла вашего
.git
папка.
...
Прохождение
Это те же шаги, что и выше, но следуйте моим точным шагам для моего репозитория вместо использования <meta-named-things>
,
Вот проект, который я имею для реализации модулей браузера JavaScript в узле:
tree ~/Code/node-browser-compat
node-browser-compat
├── ArrayBuffer
├── Audio
├── Blob
├── FormData
├── atob
├── btoa
├── location
└── navigator
Я хочу выделить одну папку, btoa
в отдельный репозиторий git
pushd ~/Code/node-browser-compat/
git subtree split -P btoa -b btoa-only
popd
Теперь у меня есть новая ветка, btoa-only
, который только имеет коммиты для btoa
и я хочу создать новый репозиторий.
mkdir ~/Code/btoa/
pushd ~/Code/btoa/
git init
git pull ~/Code/node-browser-compat btoa-only
Затем я создаю новый репозиторий на Github или Bitbucket, или что-то еще, и добавляю это origin
(кстати, "origin" - это просто соглашение, а не часть команды - вы можете назвать это "remote-server" или как угодно)
git remote add origin git@github.com:node-browser-compat/btoa.git
git push origin -u master
Счастливый день!
Примечание: если вы создали репо с README.md
, .gitignore
а также LICENSE
, вам нужно будет сначала потянуть
git pull origin -u master
git push origin -u master
Наконец, я хочу удалить папку из большего репо
git rm -rf btoa
...
аппендикс
Последний мерзавец на OS X
Чтобы получить последнюю версию git:
brew install git
Чтобы получить варев для OS X:
Последний мерзавец на 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, а просто подтверждает, что их больше нет. Если вы хотите на самом деле удалить исторические ссылки (т.е. у вас есть подтвержденный пароль), вам нужно сделать это:
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
Тем не менее, я бы порекомендовал не выполнять эти шаги, если вы не знаете, что вам нужно - просто на тот случай, если вы удалили неправильный подкаталог, понимаете? Файлы резервных копий не должны быть клонированы, когда вы нажимаете репо, они просто будут в вашей локальной копии.
кредит
Ответ Пола создает новый репозиторий, содержащий /ABC, но не удаляет / ABC из /XYZ. Следующая команда удалит / ABC из /XYZ:
git filter-branch --tree-filter "rm -rf ABC" --prune-empty HEAD
Конечно, сначала протестируйте его в репозитории 'clone --no-hardlinks' и следуйте за ним с помощью команд reset, gc и prune, которые перечисляет Пол.
Я обнаружил, что для того, чтобы правильно удалить старую историю из нового репозитория, вам нужно проделать еще немного работы после filter-branch
шаг.
Делаем клон и фильтр
git clone --no-hardlinks foo bar; cd bar git filter-branch --subdirectory-filter subdir/you/want
Удалить все ссылки на старую историю. "Origin" отслеживал ваш клон, а "original" - это то, где filter-branch сохраняет старые данные:
git remote rm origin git update-ref -d refs/original/refs/heads/master git reflog expire --expire=now --all
Даже сейчас ваша история может застрять в пакете, который fsck не коснется. Разорвите его на части, создав новый файл пакета и удалив неиспользуемые объекты:
git repack -ad
При беге git filter-branch
используя более новую версию git
(2.22+
может быть?), он говорит использовать этот новый инструмент git-filter-repo. Этот инструмент, безусловно, упростил мне задачу.
Фильтрация с помощью filter-repo
Команды для создания XYZ
репо из исходного вопроса:
# create local clone of original repo in directory XYZ
tmp $ git clone git@github.com: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 git@github.com: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
Примечания:- Я думал, что сообщение в блоге 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)
Изменить: 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
Это уже не так сложно, вы можете просто использовать команду git filter-branch для клона вашего репозитория, чтобы отбросить ненужные вам подкаталоги, а затем отправить их на новый пульт.
git filter-branch --prune-empty --subdirectory-filter <YOUR_SUBDIR_TO_KEEP> master
git push <MY_NEW_REMOTE_URL> -f .
Вот небольшая модификация CoolAJ86 "The Easy Way™", чтобы разделить несколько подпапок (скажем, sub1
а также sub2
) в новый репозиторий git.
Easy Way™ (несколько подпапок)
Подготовьте старый репо
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
, Кроме того, не используйтеmv
команда ноmove
,Конечное примечание: уникальная и большая разница с базовым ответом - вторая строка сценария "
git filter-branch...
"Создать новый репо
mkdir <new-repo> pushd <new-repo> git init git pull </path/to/big-repo> <name-of-new-branch>
Свяжите новый репо с Github или где угодно
git remote add origin <git@github.com:my-user/new-repo.git> git push origin -u master
Очистка, если желательно
popd # get out of <new-repo> pushd <big-repo> git rm -rf <name-of-folder>
Примечание. Это оставляет все исторические ссылки в репозитории. См. Приложение в исходном ответе, если вы действительно обеспокоены тем, что взяли пароль, или вам необходимо уменьшить размер файла вашего файла.
.git
папка.
Обновление: модуль git-subtree оказался настолько полезным, что команда git запустила его в ядро и сделала его git subtree
, Смотрите здесь: Отсоединить (переместить) подкаталог в отдельный Git-репозиторий
Git-поддерево может быть полезно для этого
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/
Исходный вопрос хочет, чтобы XYZ/ABC/(* файлы) стали ABC/ABC/(* файлами). После реализации принятого ответа для моего собственного кода, я заметил, что он на самом деле меняет XYZ/ABC/(* файлы) на ABC / (* файлы). Страница руководства ветки фильтра даже говорит:
Результат будет содержать этот каталог (и только этот) в качестве корня проекта."
Другими словами, он продвигает папку верхнего уровня "вверх" на один уровень. Это важное различие, потому что, например, в моей истории я переименовал папку верхнего уровня. Продвигая папки "вверх" на один уровень, git теряет непрерывность при коммите, где я сделал переименование.
Мой ответ на этот вопрос заключается в том, чтобы сделать 2 копии репозитория и вручную удалить папки, которые вы хотите сохранить в каждой. Страница man поддерживает меня с этим:
[...] избегайте использования [этой команды], если для решения вашей проблемы достаточно простого коммита
Похоже, что большинство (все?) Ответов здесь полагаются на некоторую форму git filter-branch --subdirectory-filter
и тому подобное. Это может работать "большинство раз", однако в некоторых случаях, например, когда вы переименовали папку, например:
ABC/
/move_this_dir # did some work here, then renamed it to
ABC/
/move_this_dir_renamed
Если вы используете обычный стиль фильтра git для извлечения "move_me_renamed", вы потеряете историю изменений файла, которая произошла с того момента, когда она изначально была move_this_dir ( ref).
Таким образом, кажется, что единственный способ действительно сохранить всю историю изменений (если у вас такой случай), это, по сути, скопировать хранилище (создать новое хранилище, установить его в качестве источника), а затем уничтожить все остальное. и переименуйте подкаталог в родительский файл следующим образом:
- Клонировать многомодульный проект локально
- Филиалы - проверьте что там:
git branch -a
- Выполните проверку каждой ветви, которая будет включена в разделение, чтобы получить локальную копию на вашей рабочей станции:
git checkout --track origin/branchABC
- Сделайте копию в новом каталоге:
cp -r oldmultimod simple
- Перейдите в новую копию проекта:
cd simple
- Избавьтесь от других модулей, которые не нужны в этом проекте:
git rm otherModule1 other2 other3
- Теперь остается только подкаталог целевого модуля
- Избавьтесь от поддиректории модуля, чтобы корень модуля стал новым корнем проекта.
git mv moduleSubdir1/* .
- Удалить подпапку с реликвиями:
rmdir moduleSubdir1
- Проверьте изменения в любой момент:
git status
- Создайте новый репозиторий git и скопируйте его URL, чтобы указать на него этот проект:
git remote set-url origin http://mygithost:8080/git/our-splitted-module-repo
- Убедитесь, что это хорошо:
git remote -v
- Нажмите изменения до удаленного репо:
git push
- Зайдите в удаленный репозиторий и проверьте, все ли там
- Повторите это для любой другой необходимой ветки:
git checkout branch2
Это следует за github doc "Разделение подпапки в новый репозиторий" шаги 6-11, чтобы подтолкнуть модуль к новому репо.
Это не сэкономит вам места в папке.git, но сохранит всю историю изменений этих файлов даже при переименовании. И это может не стоить того, если не будет потеряно "много" истории и т. Д. Но по крайней мере вы гарантированно не потеряете старые коммиты!
Чтобы добавить к ответу Пола, я обнаружил, что для окончательного восстановления пространства мне нужно поместить HEAD в чистый репозиторий, и это сокращает размер каталога.git/objects/pack.
т.е.
$ mkdir... ABC.git $ cd... ABC.git $ git init --bare
После gc prune также выполните:
$ 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
Правильный путь сейчас заключается в следующем:
git filter-branch --prune-empty --subdirectory-filter FOLDER_NAME [first_branch] [another_branch]
GitHub теперь даже имеет небольшую статью о таких случаях.
Но не забудьте сначала клонировать исходное хранилище в отдельный каталог (так как это приведет к удалению всех файлов и других каталогов, и вам, вероятно, придется работать с ними).
Итак, ваш алгоритм должен быть:
- клонировать ваше удаленное хранилище в другой каталог
- с помощью
git filter-branch
оставил только файлы в каком-то подкаталоге, нажмите на новый пульт - создать коммит, чтобы удалить этот подкаталог из вашего исходного удаленного репо
Я рекомендую руководство GitHub по расщеплению подпапок в новом хранилище. Этапы аналогичны ответу Пола, но я обнаружил, что их инструкции легче понять.
Я изменил инструкции, чтобы они применялись для локального репозитория, а не для размещения на GitHub.
Разбиение подпапки в новый репозиторий
Откройте Git Bash.
Измените текущий рабочий каталог на каталог, в котором вы хотите создать свой новый репозиторий.
Клонируйте репозиторий, содержащий подпапку.
git clone OLD-REPOSITORY-FOLDER NEW-REPOSITORY-FOLDER
- Измените текущий рабочий каталог на ваш клонированный репозиторий.
cd REPOSITORY-NAME
- Чтобы отфильтровать подпапку из остальных файлов в хранилище, запустите
git filter-branch
, предоставляя эту информацию:
FOLDER-NAME
Папка в вашем проекте, из которой вы хотите создать отдельный репозиторий.
- Совет: пользователи Windows должны использовать
/
разграничить папки.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
У меня была именно эта проблема, но все стандартные решения, основанные на git filter-branch, были чрезвычайно медленными. Если у вас небольшой репозиторий, то это может не быть проблемой, это было для меня. Я написал другую программу фильтрации git, основанную на libgit2, которая в качестве первого шага создает ветки для каждой фильтрации первичного репозитория, а затем отправляет их для очистки репозиториев в качестве следующего шага. В моем репозитории (500Mb 100000 коммитов) стандартные методы git filter-branch заняли несколько дней. Моя программа занимает минуты, чтобы сделать ту же самую фильтрацию.
Он имеет невероятное имя git_filter и живет здесь:
https://github.com/slobobaby/git_filter
на GitHub.
Надеюсь это кому-нибудь пригодится.
Используйте эту команду фильтра для удаления подкаталога, сохраняя при этом ваши теги и ветви:
git filter-branch --index-filter \
"git rm -r -f --cached --ignore-unmatch DIR" --prune-empty \
--tag-name-filter cat -- --all
Для чего это стоит, вот как использовать GitHub на компьютере с Windows. Допустим, у вас есть клонированный репо, проживающий в C:\dir1
, Структура каталогов выглядит следующим образом: C:\dir1\dir2\dir3
, dir3
каталог, который я хочу быть новым отдельным репо.
Github:
- Создайте свой новый репозиторий:
MyTeam/mynewrepo
Bash Prompt:
$ cd c:/Dir1
$ git filter-branch --prune-empty --subdirectory-filter dir2/dir3 HEAD
Возвращается:Ref 'refs/heads/master' was rewritten
(fyi: dir2 / dir3 чувствителен к регистру.)$ git remote add some_name git@github.com:MyTeam/mynewrepo.git
git remote add origin etc
, не работал, вернулсяremote origin already exists
"$ git push --progress some_name master
Как я упоминал выше, мне пришлось использовать обратное решение (удаление всех коммитов, не касаясь моего dir/subdir/targetdir
), который, казалось, работал довольно хорошо, удаляя около 95% коммитов (по желанию). Однако остаются две небольшие проблемы.
ПЕРВЫЙ, filter-branch
проделал большую работу по удалению коммитов, которые вводят или модифицируют код, но, очевидно, коммиты слияния находятся ниже его станции в Gitiverse.
Это косметическая проблема, с которой я, вероятно, могу смириться (он говорит... медленно отступая с отведенными глазами).
ВТОРОЕ несколько оставшихся коммитов в значительной степени дублируются! Я, кажется, приобрел второй, избыточный график времени, который охватывает почти всю историю проекта. Интересная вещь (которую вы можете видеть из рисунка ниже) состоит в том, что мои три локальных ветви не все находятся на одной и той же временной шкале (что, разумеется, почему оно существует, а не просто сбор мусора).
Единственное, что я могу себе представить, это то, что одним из удаленных коммитов был, возможно, единственный коммит слияния, который filter-branch
фактически удалил, и это создало параллельную временную шкалу, так как каждая теперь неопакованная нить получала свою собственную копию коммитов. (пожимает плечами Где мои ТАРДИ?) Я почти уверен, что смогу решить эту проблему, хотя мне бы очень хотелось понять, как это произошло.
В случае сумасшедшего mergefest-O-RAMA, я, скорее всего, оставлю этот в покое, так как он так прочно укоренился в моей истории коммитов - угрожая мне всякий раз, когда я подхожу, - кажется, это не вызывает любые не косметические проблемы и потому что это довольно симпатично в Tower.app.
Более легкий путь
- устанавливать
git splits
, Я создал его как расширение git, основанное на решении jkeating. Разделите каталоги в местный филиал
#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Создайте пустой репо где-нибудь. Предположим, мы создали пустой репо с именем
xyz
на GitHub, который имеет путь:git@github.com:simpliwp/xyz.git
Нажмите на новый репо.
#add a new remote origin for the empty repo so we can push to the empty repo on GitHub git remote add origin_xyz git@github.com:simpliwp/xyz.git #push the branch to the empty repo's master branch git push origin_xyz XYZ:master
Клонировать только что созданный удаленный репо в новый локальный каталог
#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 git@github.com:simpliwp/xyz.git
Проверьте проект 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.
Вам может понадобиться что-то вроде "git reflog expire --expire=now --all" перед сборкой мусора, чтобы фактически удалить файлы. git filter-branch просто удаляет ссылки в истории, но не удаляет записи reflog, в которых хранятся данные. Конечно, сначала проверьте это.
Использование моего диска резко упало при этом, хотя мои начальные условия были несколько иными. Возможно --subdirectory-filter отрицает эту необходимость, но я сомневаюсь в этом.
Поместите это в ваш 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'
Вы можете легко попробовать https://help.github.com/enterprise/2.15/user/articles/splitting-a-subfolder-out-into-a-new-repository/
Это сработало для меня. Проблемы, с которыми я столкнулся на приведенных выше этапах:
в этой команде
git filter-branch --prune-empty --subdirectory-filter FOLDER-NAME BRANCH-NAME
BRANCH-NAME
хозяинесли последний шаг завершается неудачно при фиксации из-за проблем защиты, следуйте - https://docs.gitlab.com/ee/user/project/protected_branches.html
Я уверен, что с поддеревом git все в порядке и замечательно, но мои подкаталоги управляемого кода git, которые я хотел переместить, все были в затмении. Так что, если вы используете egit, это больно легко. Возьмите проект, который вы хотите переместить, и объедините-> отключите его, а затем объедините-> поделитесь им с новым местоположением. По умолчанию будет пытаться использовать старое место репо, но вы можете снять отметку с уже существующего выбора и выбрать новое место для его перемещения. Приветствую вас.
Я нашел довольно простое решение, идея состоит в том, чтобы скопировать хранилище, а затем просто удалить ненужную часть. Вот как это работает:
1) Клонируйте репозиторий, который вы хотите разбить
git clone git@git.thehost.io: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 git@github.com:username/new_repo
git push -u origin master
Вот и все. Вы можете повторить шаги, чтобы получить другой репозиторий,
просто удалите XY1,XY2 и переименуйте XYZ -> ABC на шаге 3
Нашел эту замечательную статью. Оригинальная ссылка, которую легко понять. Документирование здесь на случай, если он станет недоступен.
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 git@github.com: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
Наконец, мы возвращаемся в корневой каталог, удаляем папку из нашего основного репозитория, затем фиксируем и отправляем изменение. Теперь у нас есть папка в нашем основном репозитории, но она связана с совершенно отдельным репозиторием, который можно повторно использовать в нескольких проектах.