Возможно использование флеш-файлов cookie :
Вам нужно будет создать небольшой (скрытый) флеш-фильм для чтения и записи.
Независимо от выбранного маршрута, убедитесь, что ваши пользователи выбирают IN для отслеживания, иначе вы «вторгаются в их личную жизнь и становятся одним из плохих парней.
Обновление : Этот процесс так распространен, что команда мерзавца сделала его намного более простым с новым инструментом, git subtree
. Посмотрите здесь: Отсоединение (перемещение) подкаталог в отдельный репозиторий Мерзавца
Вы хотите клонировать свой репозиторий и затем использовать git filter-branch
для маркировки всего кроме подкаталога, Вы хотите в своем новом repo быть собранными "мусор".
Для клонирования локального репозитория:
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
Теперь Вы могли бы хотеть также удалить теги, которые не имеют никакого отношения с подпроектом; можно также сделать это позже, но Вы, возможно, должны были бы сократить свой repo снова. Я не сделал так и добрался WARNING: Ref 'refs/tags/v0.1' is unchanged
для всех тегов (так как они были все не связаны с подпроектом); дополнительно, после удаления таких тегов больше пространства будет освобождено. По-видимому git filter-branch
должен смочь переписать другие теги, но я не мог проверить это. Если Вы хотите удалить все теги, используйте git tag -l | xargs git tag -d
.
Затем ответвление фильтра использования и сброс для исключения других файлов, таким образом, они могут быть сокращены. Давайте также добавим --tag-name-filter cat --prune-empty
для удаления пустых фиксаций, и переписать теги (обратите внимание, что это должно будет разделить их подпись):
git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC -- --all
или альтернативно, чтобы только переписать ГЛАВНОЕ ответвление и проигнорировать теги и другие ответвления:
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
, и теперь у Вас есть локальный репозиторий мерзавца подкаталога ABC со всей его сохраненной историей.
Примечание: Для большей части использования, git filter-branch
должен действительно иметь добавленный параметр -- --all
. Да это действительно - - пространство - - all
. Это должно быть последними параметрами для команды. Как обнаруженный Matli, это сохраняет ответвления проекта и отмечает включенный в новый repo.
Редактирование: различные предложения из комментариев ниже были включены, чтобы удостовериться, например, что репозиторий на самом деле уменьшается (который не всегда имел место прежде).
Я уверен, что с поддеревом git все в порядке и замечательно, но мои подкаталоги управляемого кода git, которые я хотел переместить, были в затмении. Так что если вы используете egit, это больно легко. Возьмите проект, который вы хотите переместить, и объедините его в команду> отключите его, а затем объедините команду> поделиться им в новом месте. По умолчанию будет пытаться использовать старое место репо, но вы можете снять отметку с уже существующего выбора и выбрать новое место для его перемещения. Всем привет.
Поместите это в ваш 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'
Используйте эту команду фильтра для удаления подкаталога, сохраняя при этом ваши теги и ветви:
git filter-branch --index-filter \
"git rm -r -f --cached --ignore-unmatch DIR" --prune-empty \
--tag-name-filter cat -- --all
У меня была именно эта проблема, но все стандартные решения, основанные на git filter-branch, были чрезвычайно медленными. Если у вас небольшой репозиторий, то это может не быть проблемой, это было для меня. Я написал другую программу фильтрации git, основанную на libgit2, которая в качестве первого шага создает ветки для каждой фильтрации основного репозитория, а затем отправляет их для очистки репозиториев в качестве следующего шага. В моем репозитории (500Mb 100000 коммитов) стандартные методы git filter-branch заняли несколько дней. Моя программа занимает минуты, чтобы выполнить ту же фильтрацию.
Он имеет невероятное имя git_filter и живет здесь:
https://github.com/slobobaby/git_filter
на GitHub.
Надеюсь, это кому-нибудь пригодится.
Вот как это работает, используя 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
Правильный путь теперь следующий:
git filter-branch --prune-empty --subdirectory-filter FOLDER_NAME [first_branch] [another_branch]
GitHub теперь даже имеет небольшую статью о таких случаях.
Но не забудьте сначала клонировать исходное хранилище в отдельный каталог (так как это приведет к удалению всех файлов и других каталогов, и вам, вероятно, потребуется с ними работать).
Таким образом, ваш алгоритм должен быть:
git filter-branch
, оставляя только файлы в каком-то подкаталоге, нажать на новый удаленный Я обнаружил, что для того, чтобы правильно удалить старую историю из нового репозитория, вам нужно проделать еще немного работы после шага filter-branch
.
Сделайте клон и фильтр:
git clone --no-hardlinks foo bar; cd bar
git filter-branch --subdirectory-filter subdir/you/want
Удалите все ссылки на старую историю. «Origin» отслеживал ваш клон, а «original» - это то место, где фильтр-ветвь сохраняет старые данные:
git remote rm origin
git update-ref -d refs/original/refs/heads/master
git reflog expire --expire=now --all
Даже сейчас ваша история может застревать в пакете этот fsck не будет касаться Разорвите его в клочья, создав новый файл пакета и удалив неиспользуемые объекты:
git repack -ad
объяснение этого в руководстве для фильтра ветвп .
Редактировать: добавлен скрипт 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: [111] </path/to/repo/> <directory/to/extract/> <newName>"
echo
echo "Example: [111] /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
Исходный вопрос требует, чтобы XYZ / ABC / (* файлы) стали ABC / ABC / (* файлами). После реализации принятого ответа для моего собственного кода, я заметил, что он на самом деле меняет XYZ / ABC / (* файлы) на ABC / (* файлы). Страница руководства ветки фильтра даже говорит:
Результат будет содержать этот каталог (и только этот) в качестве корня проекта . "
Другими словами, он продвигает папку верхнего уровня «вверх» на один уровень. Это важное различие, потому что, например, в моей истории я переименовал папку верхнего уровня. Продвигая папки «вверх» на один уровень, git теряет непрерывность на коммите, где я сделал переименование.
Мой ответ на этот вопрос заключается в том, чтобы сделать 2 копии репозитория и вручную удалить папки, которые вы Я хочу сохранить в каждом. Страница man поддерживает меня следующим образом:
[...] избегайте использования [этой команды], если достаточно простого коммита для решения вашей проблемы
Это уже не так сложно, вы можете просто использовать команду git filter-branch на клоне вашего репозитория, чтобы отбросить ненужные вам подкаталоги, а затем отправить их на новый пульт.
git filter-branch --prune-empty --subdirectory-filter <YOUR_SUBDIR_TO_KEEP> master
git push <MY_NEW_REMOTE_URL> -f .
Обновление : Модуль 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 -проекты-с-ГИТ-поддерево /
Ответ Пола создает новый репозиторий, содержащий / ABC, но не удаляет / ABC из / XYZ. Следующая команда удалит / ABC из / XYZ:
git filter-branch --tree-filter "rm -rf ABC" --prune-empty HEAD
Конечно, сначала протестируйте его в репозитории 'clone --no-hardlinks' и следуйте за ним с помощью команд reset, gc и prune Пол списков.
Как я упомянул выше , мне пришлось использовать обратное решение (удаление всех коммитов, не касаясь моего dir/subdir/targetdir
), которое, казалось, работало довольно хорошо, удаляя около 95% коммитов (по желанию). Однако остаются две небольшие проблемы.
ПЕРВЫЙ , filter-branch
проделал большую работу по удалению коммитов, которые вводят или модифицируют код, но, очевидно, коммитов слияния находятся ниже его места в Gitiverse.
Это косметическая проблема, с которой я, вероятно, могу жить ( он говорит ... отступая медленно с отведенными глазами) .
ВТОРОЕ несколько оставшихся коммитов в значительной степени ВСЕ дублированы! Кажется, я приобрел второй, избыточный график, который охватывает почти всю историю проекта. Интересная вещь (которую вы можете увидеть из рисунка ниже) заключается в том, что мои три локальные ветви не все находятся на одной временной шкале (что, разумеется, почему оно существует, а не просто сбор мусора).
Единственное, что я могу себе представить, это то, что один из удаленных Коммиты, возможно, были единственным коммитом слияния, который filter-branch
фактически удалил , и который создал параллельную временную шкалу, поскольку каждая теперь не слитая цепь получила свою собственную копию коммитов. ( пожимает плечами Где мои ТАРДИ?) Я почти уверен, что смогу решить эту проблему, хотя я очень очень хотел бы понять, как это произошло.
В случае сумасшедшего mergefest-O-RAMA я, скорее всего, оставлю его в покое, так как он так прочно укоренился в моей истории коммитов - угрожая мне, когда я подхожу, - кажется, это не так. на самом деле вызывает какие-то не косметические проблемы и потому что это довольно симпатично в Tower.app.
Вот небольшая модификация CoolAJ86 "The Easy Way & trade;" ответьте , чтобы разбить несколько подпапок (скажем, sub1
и sub2
) в новый репозиторий git.
Подготовить старое репо
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 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 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, но сохранит всю историю изменений этих файлов даже при переименовании. И это может не стоить того, если не будет потеряно «много» истории и т. Д. Но, по крайней мере, вы гарантированно не потеряете старые коммиты!
При выполнении git filter-branch
использование более новой версии git
(2.22+
, возможно?), это говорит для использования этого нового инструмента git-filter-repo. Этот инструмент, конечно, упростил вещи для меня.
Команды для создания XYZ
repo от исходного вопроса:
# 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 repo был новым и пустым перед нажатием
В моем случае, я также хотел переместить несколько каталогов для более последовательной структуры. Первоначально, я выполнил то простое filter-repo
команда, сопровождаемая [1 110], но я нашел, что мог получить немного "лучшую" историю с помощью --path-rename
опция. Вместо того, чтобы видеть в последний раз изменил 5 hours ago
на перемещенных файлах в новом repo, который я теперь вижу last year
(в UI GitHub), который соответствует измененным временам в исходном repo.
Вместо...
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 filter-repo --subdirectory-filter dir-matching-new-repo-name
). Та команда правильно преобразовала тот подкаталог в корень скопированного локального repo, но это также привело к истории только трех фиксаций, которые потребовалось для создания подкаталога. (Я не понял, что --path
мог быть указан многократно; таким образом, устраняя потребность создать подкаталог в источнике repo.), Так как кто-то согласился на источник repo к тому времени, когда я заметил, что мне не удалось продвинуть историю, я просто использовал 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
ln -s ~/github/newren/git-filter-repo/git-filter-repo $(git --exec-path)
Вам может понадобиться что-то вроде "git reflog expire --expire = now --all" перед сборкой мусора, чтобы фактически очистить файлы. git filter-branch просто удаляет ссылки в истории, но не удаляет записи журнала ссылок, содержащие данные. Конечно, сначала проверьте это.
При этом резко упало использование моего диска, хотя мои начальные условия были несколько иными. Возможно, --subdirectory-filter отменяет эту необходимость, но я сомневаюсь в этом.
Чтобы добавить к ответу Пола , я обнаружил, что для окончательного восстановления места мне нужно отправить HEAD в чистый репозиторий, что уменьшает размер .git / каталог objects / pack.
ie
$ 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