Git копирование файлов между репозиториями с историей изменений
Недавно я столкнулся с задачей переноса папки с проектом из одного репозитория в другой на github. Звучит примитивно, но если рассмотреть то, что дано и то, что необходимо получить, могут возникнуть некоторые нюансы.
- Есть большой репозиторий, содержащий множество папок. Каждая папка – это отдельный проект.
Что необходимо сделать:
- Одну из папок перенести в отдельный репозиторий с сохранением ее истории коммитов.
В теории можно было бы просто скопировать весь репозиторий со всем содержимым в новое место, а потом просто удалить те папки, которые не нужны. Но такой способ довольно неоптимален и не особо мне понравился, так что я решил поступить иначе.
Я использовал стандартный гитовый filter-branch. За основу я взял следующие статьи:
В этом посте я хочу немного адаптировать процесс для лучшего восприятия.
Предположим для примера, что наш репозиторий называется movement-example , а та единственная папка, которую мы хотим перенести в отдельный репозиторий – folder-to-move . Тогда шаги, которые необходимо выполнить для подготовки переноса, выглядят следующим образом:
Первая часть готова, а вторая заключается в том, чтобы совершить фактический перенос. Предположим, что новый репозиторий называется просто new-repo , тогда необходимые шаги выглядят следующим образом:
Готово! Теперь в вашем новом репозитории есть только интересующая вас папка вместе со всей историей коммитов. Например, сразу после вышеописанных шагов я увидел следующее в своем новом репозитории:
Кстати, вы увидите только 1 бранч – master . Процедура переносит только один бранч за раз. Если вы хотите перенести dev , то вам нужно просто сделать git checkout dev и git pull origin dev после второго шага на обоих этапах.
Если вам нужно перенести все 50 (60? 100?) бранчей, то данное решение не будет удачным из-за слишком большого количества рутинной работы. Но я считаю, что для переноса достаточно лишь сохранить master и dev бранчи, потому что все feature branches уже должны быть в dev, а новые бранчи вы будете ветвить уже в новом репозитории.
UPDATE
Спасибо fstep за подсказку. Можно просто воспользоваться другой гитовой утилитой – subtree . Для этого нужно всего ничего:
Ну, а если new-repo является совсем свежим и не содержит даже readme файла – другими словами, вообще не содержит файлов, то все еще проще:
У меня есть два репозитория git, которые говорят A и B, оба содержат файл с именем file1.cc.
Возможно ли объединить/скопировать историю file1.cc в repo A в файл1.cc в repo B?
Проблема в том, что мы уже переместили файлы из репо A в репо B и история всех файлов была потеряна. но теперь некоторые из разработчиков уже начали работу над репо B и внесли свои изменения. Поэтому теперь я хочу объединить/скопировать историю некоторых файлов из repo A в repo B и которые применимы только для некоторых файлов. Можно ли это сделать? Или история потерянных файлов теряется навсегда?
Пожалуйста, помогите. Спасибо заранее.
Независимо от того, что вы переходите из Repo A в Repo B, может быть, есть какая-то история, которую вы специально хотите удалить. Это потенциально целая тема, но позвольте предположить, что вы действительно хотите только историю нескольких файлов.
В специальном случае, когда все нужные файлы (и другие) находятся в подкаталоге и вы хотите (или, по крайней мере, можете принять) переместить эти файлы в корневой каталог репо, вы можете использовать filter-branch с помощью --subdirectory-filter .
В более общем плане, если мы предполагаем, что пути не должны меняться и что файлы, которые вы хотите, могут быть в любом месте дерева, тогда вы можете использовать filter-branch с --index-filter .
Это может занять некоторое время, если у репо было много коммитов. Если список файлов в rm не является тривиальным, вы можете поместить несколько команд git rm в оболочку script и использовать это как аргумент --index-filter вместо того, чтобы вставлять его, как показано выше.
Хорошо, так или иначе, надеюсь, у вас есть история, которую вы хотели бы пересадить в Repo B.
Теперь у вас в репо B:
Теперь у вас есть три варианта: переименовать, переустановить или заменить
где $commit-id является SHA для B , а $graft-id является SHA для C
Ребаза может быть немного проще (при условии определенного уровня согласованности между историями), но вводит возможность, что вы в конечном итоге измените дерево на D . Если вы решите попробовать rebase, это будет
а затем отбросить запись для B' .)
Чтобы поддержать это, вы просто помечаете B (и, возможно, также B' ):
Когда кто-то клонирует репо, они будут видеть только новую историю, но они могут сказать
и это приведет к перерыву в истории.
После того, как вы выполнили повторную, перезагрузку или замену, вы можете удалить удаленный пульт repo-a .
Я пытаюсь переместить только содержимое одного репозитория ( repo1 ) в другой существующий репозиторий ( repo2 ) с помощью следующих команд:
Но это не работает. Я просмотрел аналогичный пост, но нашел только одно перемещение папки, а не содержимое.
Хотите ли вы иметь содержимое repo1 как ветку на repo2 или как часть master, чтобы папки обоих репо сосуществовали в вашем рабочем каталоге?Я думаю, что команды, которые вы ищете:
После этого repo2/master будет содержать все от repo2/master и repo1/master , а также будет иметь историю обоих.
Спасибо Chronial, я просто хочу убедиться, что у repo1 также есть одна ветка, и что, если я хочу переместить эту ветку также в repo2? хорошо, у repo2 также есть вторая ветка? Или что должно случиться с этой веткой? Хронический, вышеупомянутые команды не работали! это не скопировало историю. Хорошо, теперь это сработало. Я использовал следующую команду: cd repo2,> git remote rm origin> git remote add origin url-of-repo1 ,> git fetch r1remote> git push origin origin master @abhijithda Самое простое решение состоит в том, чтобы просто переместить все внутри repo1 подпапки (внутри repo1 ) перед выполнением слияния.Сначала мы должны извлечь все удаленные ветви и теги из существующего репозитория в наш локальный индекс:
Мы можем проверить все недостающие ветви, которые нам нужны для создания локальной копии:
Давайте используем SSH-клонированный URL нашего нового репозитория, чтобы создать новый удаленный в нашем существующем локальном репозитории:
Давайте сделаем new-origin удаленным по умолчанию:
Переименуйте new-origin только в origin, чтобы он стал удаленным по умолчанию:
Можно подтвердить, что это копирует всю историю и теги. Очень хорошо. Спасибо. Если у нового источника нет никаких коммитов, это работает хорошо. Однако, если есть какой-либо коммит в new-origin, упомянутый здесь способ не будет соответствовать ожидаемому. вам нужно переключить git checkout BranchName ветки с помощью push push на удаленное репо, git push --all new-origin но большое спасибоЕсли вы хотите сохранить существующие ветки и зафиксировать историю, вот один способ, который сработал для меня.
Теперь предположим, что вы хотите синхронизировать исходные и конечные репозитории в течение определенного периода времени. Например, в текущем удаленном репо все еще есть активность, которую вы хотите перенести в новое / замененное репо.
Чтобы получить последние обновления (при условии, что у вас нет локальных изменений):
NB: Я еще не использовал субмодули, поэтому я не знаю, какие дополнительные шаги могут потребоваться, если они у вас есть.
Это сработало просто и легко для меня. Я думаю, что это должен быть проверенный ответ. Если у вас есть 60 филиалов, это путь.Самый простой подход, если Git уже отслеживает код, а затем установить новый репозиторий в качестве «источника», на который нужно нажать.
Время - лучший учитель! Жаль, что оно убивает своих учеников.
© 2019. All rights reserved.
Вот картинка того, что требуется сделать:
Решить эту проблему можно тремя способами:
- Скопировать весь локальный репозиторий одной стороны (например, Repo A) и перенести его на флэшке рядом с репозиторием (Repo B) чтобы между ними можно было установить прямую связь через локальную файловую систему
- Использовать механизм патчей (git am)
- Использовать механизм пакетов git’а (git bundles)
Первый способ нам не подошел. Хотя бы потому, что репозиторий весит около 1.5 ГБ в сжатом виде. А заливать его приходилось в том числе через RDP соединение. Хотя если у вас есть такая возможность – это самый правильный и самый лучший вариант. Нам увы не подошел, поэтому идем дальше.
Второй способ мы даже активно использовали. Workflow там примерно следующий. С какого-то определенного коммита мы делаем набор патчей для каждого коммита выбранной ветки. На другом репозитории мы переключаемся (или создаем если такой ветки еще нет) на нужную ветку и делаем Apply Patch Serial. В принципе это все работает, но есть проблемы. Проблема первая – фактически в двух репозиториях мы имеем две разные ветки, хоть они одинаково и называются. И содержат разные коммиты, хоть и они содержат одно и то же. Кроме того, возникают нетривиальные вещи связанные с разрешением коллизий. Вообщем, достаточно громоздко и сложно, хотя и работает.
Третий способ – git bundles. Вот его и рассмотрим.
Начнем с того, что git bundle – это такой специальный файл. В который во-первых упакованы нужные ветки и нужные коммиты (вы их указываете сами). Во-вторых, он может представляться как удаленный git-репозиторий, который можно добавить в remotes и работать с ним как с полноценным удаленным репозиторием. Скажем так – этот подход – лайт версия первого подхода, когда вы тащите за собой весь репозиторий. Только здесь вы тащите один файл с тем, что надо. Места он правда может занять тоже весьма нехило, но в общем гораздо меньше (при правильном подходе), чем весь репозиторий.
Теперь давайте рассмотрим как это дело провернуть. Предположим у вас есть репозиторий A из которого необходимо перетащить ветку в репозиторий B (который пуст).
Имеем несколько файлов в репозитории A:
Создание бандлов
Теперь нам нужно создать (пересоздать) бандл. Тут два пути – если он уже был создан когда-то, или же его еще не было.
Случай, когда мы создаем новый бандл
Случай, если бандл уже создавали (и появились новые изменения)
Разворачивание бандлов
Теперь бандл у нас есть, необходимо развернуть (обновить) его на другом репозитории RepoB.
В случае если бандл мы принесли первый раз и репозитория нет.
Тогда можно просто склонировать репозиторий прямо с бандла:
Ветка автоматически разворачивается до текущего состояния (флаг -b master указывает нужную ветку)
Случай, когда бандл принесли первый раз, но репозиторий уже есть
Тут несколько сложнее, необходимо зайти в папку .git в корне репозитория и отредактировать там файл config
После этого делаем
И получаем выгруженную историю из бандла.
Случай, когда бандл уже приносили
В этом случае достаточно заменить старую версию бандла новым, и сделать git pull. Бандлы, сделанные не с самого «начала времен» а с определенного места занимают не так много места.
Надеюсь всем было все понятно и это поможет вам вести удобную разработку с использованием git’а.
Читайте также: