Наши Git-репозитории начинались как части одного монстра SVN-репозитория, где у каждого проекта было свое дерево, например:
project1/branches
/tags
/trunk
project2/branches
/tags
/trunk
Очевидно, что было довольно легко перемещать файлы с одного на другой svn mv
. Но в Git каждый проект находится в своем собственном репозитории, и сегодня меня попросили переместить подкаталог из project2
в project1
. Я сделал что-то вроде этого:
$ git clone project2
$ cd project2
$ git filter-branch --subdirectory-filter deeply/buried/java/source/directory/A -- --all
$ git remote rm origin # so I don't accidentally overwrite the repo ;-)
$ mkdir -p deeply/buried/different/java/source/directory/B
$ for f in *.java; do
> git mv $f deeply/buried/different/java/source/directory/B
> done
$ git commit -m "moved files to new subdirectory"
$ cd ..
$
$ git clone project1
$ cd project1
$ git remote add p2 ../project2
$ git fetch p2
$ git branch p2 remotes/p2/master
$ git merge p2 # --allow-unrelated-histories for git 2.9+
$ git remote rm p2
$ git push
Но это кажется довольно запутанным. Есть ли лучший способ сделать такое вообще? Или я принял правильный подход?
Обратите внимание, что это включает в себя объединение истории в существующее хранилище, а не просто создание нового автономного хранилища из части другого ( как в предыдущем вопросе ).
git
repository
ebneter
источник
источник
git fetch p2 && git merge p2
вместоgit fetch p2 && git branch .. && git merge p2
? Изменить: хорошо, похоже, вы хотите получить изменения в новой ветви с именем p2, а не текущей ветви.--allow-unrelated-histories
к последнему,git merge
чтобы это работало.Ответы:
Да, попав на
--subdirectory-filter
изfilter-branch
было ключевым. Тот факт, что вы использовали его, по существу доказывает, что нет более простого способа - у вас не было выбора, кроме как переписать историю, поскольку вы хотели получить только (переименованное) подмножество файлов, и это по определению меняет хэши. Поскольку ни одна из стандартных команд (напримерpull
) не переписывает историю, вы не можете использовать их для этого.Конечно, вы могли бы уточнить детали - некоторые из ваших клонов и ветвлений не были строго необходимы - но общий подход хорош! Обидно, что это сложно, но, конечно, смысл Git не в том, чтобы упростить переписывание истории.
источник
--index-filter
руководстваfilter-branch
.Если ваша история нормальна, вы можете взять коммиты как патч и применить их в новом хранилище:
Или в одну строку
(Взято из документов Exherbo )
источник
git log --pretty=email --patch-with-stat --full-index --binary --reverse -- client > patch
. Работает без проблем AFAICT.--committer-date-is-author-date
опцию, чтобы сохранить исходную дату фиксации вместо даты, когда файлы были перемещены.git log
, так что она не работает с обоими--follow
и--reverse
правильно). Я использовал этот ответ , и вот полный сценарий, который я использую сейчас для перемещения файловПерепробовав различные подходы для перемещения файла или папки из одного Git-репозитория в другой, единственный, который, кажется, работает надежно, описан ниже.
Это включает в себя клонирование репозитория, из которого вы хотите переместить файл или папку, перемещение этого файла или папки в корень, переписывание истории Git, клонирование целевого репозитория и извлечение файла или папки с историей непосредственно в этот целевой репозиторий.
Первый этап
Сделайте копию хранилища A, так как следующие шаги вносят существенные изменения в эту копию, которые вам не следует нажимать!
CD в это
Удалите ссылку на исходный репозиторий, чтобы избежать случайного внесения каких-либо удаленных изменений (например, путем нажатия)
Просматривайте свою историю и файлы, удаляя все, что не находится в каталоге 1. В результате содержимое каталога 1 выливается в базу хранилища А.
Только для перемещения одного файла: пройдите через то, что осталось, и удалите все, кроме нужного файла. (Вам может понадобиться удалить файлы, которые вы не хотите, с тем же именем и зафиксировать.)
Второй этап
Шаг очистки
Шаг очистки
Шаг очистки
Вы можете импортировать эти файлы в хранилище B в каталоге, а не в корне:
Сделать этот каталог
Переместить файлы в этот каталог
Добавить файлы в этот каталог
Передайте ваши изменения, и мы готовы объединить эти файлы в новый репозиторий
Этап третий
Сделайте копию хранилища B, если у вас ее еще нет
(при условии, что FOLDER_TO_KEEP - это имя нового репозитория, в который вы копируете)
CD в это
Создайте удаленное соединение с хранилищем A как ветвь в хранилище B
Извлеките эту ветку (содержащую только каталог, который вы хотите переместить) в хранилище B.
Тяга копирует как файлы, так и историю. Примечание: вы можете использовать слияние вместо вытягивания, но вытягивание работает лучше.
Наконец, вы, вероятно, хотите немного очиститься, удалив удаленное соединение с хранилищем A
Нажмите и все готово.
источник
Я нашел это очень полезным. Это очень простой подход, при котором вы создаете патчи, которые применяются к новому репо. Смотрите связанную страницу для более подробной информации.
Он содержит только три шага (скопировано из блога):
Единственной проблемой, с которой я столкнулся, было то, что я не мог применить все патчи одновременно, используя
Под Windows я получил ошибку InvalidArgument. Поэтому мне пришлось применять все патчи один за другим.
источник
СОХРАНЯЯ НАИМЕНОВАНИЕ КАТАЛОГА
Фильтр подкаталога (или более короткое командное поддерево git) работает хорошо, но у меня не работает, так как они удаляют имя каталога из информации о коммите. В моем сценарии я просто хочу объединить части одного репозитория в другой и сохранить историю с полным именем пути.
Мое решение состояло в том, чтобы использовать древовидный фильтр и просто удалить ненужные файлы и каталоги из временного клона исходного репозитория, а затем вытащить этот клон в мой целевой репозиторий за 5 простых шагов.
источник
Тот, который я всегда использую, находится здесь http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/ . Просто и быстро.
Для соответствия стандартам stackoverflow, вот процедура:
источник
В этом ответе представлены интересные команды, основанные
git am
и представленные с помощью примеров, шаг за шагом.Задача
Процедура
git log --pretty=email -p --reverse --full-index --binary
git am
1. Извлечь историю в формате электронной почты
Пример: история Экстракт
file3
,file4
иfile5
Очистить временный каталог назначения
Очистите свой источник репо
Извлечь историю каждого файла в формате электронной почты
К сожалению вариант
--follow
или--find-copies-harder
нельзя сочетать с--reverse
. Вот почему история обрезается при переименовании файла (или при переименовании родительского каталога).После: Временная история в формате электронной почты
2. Реорганизовать дерево файлов и обновить изменение имени файла в истории [необязательно]
Предположим, вы хотите переместить эти три файла в этом другом репо (это может быть тот же репо).
Поэтому реорганизуйте ваши файлы:
Ваша временная история сейчас:
Измените также имена файлов в истории:
Примечание: это переписывает историю, чтобы отразить изменение пути и имени файла.
(т.е. изменение нового местоположения / имени в новом репо)
3. Применить новую историю
Ваш другой репо:
Применить коммиты из временных файлов истории:
Ваш другой репо сейчас:
использование
git status
чтобы увидеть количество коммитов, готовых к отправке :-)Примечание. Поскольку история была переписана, чтобы отразить изменение пути и имени файла:
(т.е. по сравнению с местоположением / именем в предыдущем репо)
git mv
менять местоположение / имя файла.git log --follow
доступа к полной истории.Дополнительный трюк: обнаружение переименованных / перемещенных файлов в вашем репо
Для просмотра списка файлов, которые были переименованы:
Дополнительные настройки: вы можете выполнить команду,
git log
используя параметры--find-copies-harder
или--reverse
. Вы также можете удалить первые два столбца, используяcut -f3-
полный шаблон '{. * =>. *}'.источник
Имея подобный царапин (хотя только для некоторых файлов данного репозитория), этот скрипт оказался действительно полезным: git-import
Короткая версия заключается в том, что он создает файлы исправлений для данного файла или каталога (
$object
) из существующего хранилища:которые затем применяются к новому хранилищу:
Для деталей, пожалуйста, посмотрите вверх:
источник
Попробуй это
cd repo1
Это удалит все каталоги, кроме упомянутых, сохраняя историю только для этих каталогов.
Теперь вы можете добавить свой новый репозиторий в свой git remote и добавить его к этому
добавить
-f
перезаписатьисточник
Используя вдохновение из http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/ , я создал эту функцию Powershell для того же, которая имеет до сих пор отлично работал для меня:
Использование для этого примера:
После того, как вы это сделали, вы можете реорганизовать файлы в
migrate-from-project2
ветке, прежде чем объединять ее.источник
Я хотел что-нибудь надежное и многократно используемое (функция «одна команда и иди + отменить»), поэтому я написал следующий скрипт bash. Несколько раз работал на меня, поэтому я решил поделиться этим здесь.
Он может перемещать произвольную папку
/path/to/foo
изrepo1
в/some/other/folder/bar
вrepo2
(пути к папкам могут быть одинаковыми или разными, расстояние от корневой папки может быть разным).Так как он распространяется только на коммиты, которые касаются файлов во входной папке (не на все коммиты исходного репо), он должен быть достаточно быстрым даже для больших репозиториев, если вы просто извлекаете глубоко вложенную подпапку, которая не была затронута в каждом совершить.
Поскольку для этого нужно создать потерянную ветку со всей историей старого репо, а затем объединить ее с ГОЛОВКОЙ, она будет работать даже в случае столкновения имен файлов (тогда вам, конечно, придется разрешить объединение в конце) ,
Если нет конфликтов имен файлов, вам просто нужно
git commit
в конце завершить объединение.Недостатком является то, что он, скорее всего, не будет следовать переименованиям файлов (вне
REWRITE_FROM
папки) в исходных репо-запросах, которые GitHub приветствует для этого.Ссылка на GitHub: git-move-folder -ween-repos-keep-history
источник
В моем случае мне не нужно было сохранять репозиторий, из которого я мигрировал, или сохранять любую предыдущую историю. У меня был патч той же ветки, с другого пульта
На этих двух шагах я смог заставить филиал другого репо появляться в том же репо.
Наконец, я установил эту ветку (которую я импортировал из другого репо) так, чтобы она соответствовала основной линии целевого репо (чтобы я мог точно их различать)
Теперь он вел себя так, как будто это была просто еще одна ветка, которую я толкнул против того же репо.
источник
Если пути для указанных файлов одинаковы в двух репозиториях, и вы хотите перенести только один файл или небольшой набор связанных файлов, один из простых способов сделать это - использовать
git cherry-pick
.Первый шаг - перенести коммиты из другого репо в ваше локальное репо с помощью
git fetch <remote-url>
. Это оставитFETCH_HEAD
указание на главный коммит из другого репо; если вы хотите сохранить ссылку на этот коммит после выполнения других выборок, вы можете пометить его тегомgit tag other-head FETCH_HEAD
.Затем вам нужно будет создать первоначальный коммит для этого файла (если он не существует) или коммит, чтобы привести файл в состояние, которое может быть исправлено первым коммитом из другого репо, который вы хотите ввести. Вы можете быть в состоянии сделать это с помощью,
git cherry-pick <commit-0>
еслиcommit-0
вы представили нужные файлы, или вам может понадобиться создать коммит «вручную». Добавить-n
к параметрам cherry-pick, если вам нужно изменить первоначальный коммит, например, удалить файлы из того коммита, который вы не хотите вносить.После этого вы можете перейти к
git cherry-pick
последующим коммитам, снова используя-n
при необходимости. В простейшем случае (все коммиты именно то , что вы хотите и применять чисто) , вы можете дать полный список коммитов в командной строке вишневой выбрать:git cherry-pick <commit-1> <commit-2> <commit-3> ...
.источник
Это становится проще благодаря использованию git-filter-repo.
Для того, чтобы перейти
project2/sub/dir
кproject1/sub/dir
:Чтобы установить инструмент просто:
pip3 install git-filter-repo
( более подробную информацию и опции в README )источник
Приведенный ниже метод переноса моего GIT Stash в GitLab с сохранением всех веток и сохранением истории.
Клонировать старый репозиторий в локальный.
Создайте пустой репозиторий в GitLab.
Выше я выполнил, когда мы перенесли наш код из stash в GitLab, и он работал очень хорошо.
источник