Как импортировать существующий репозиторий GIT в другой?

StackOverflow https://stackoverflow.com/questions/1683531

  •  18-09-2019
  •  | 
  •  

Вопрос

У меня есть репозиторий GIT в папке под названием XXX, и у меня есть второй репозиторий GIT под названием Yyy.

Я хочу импортировать XXX хранилище в Yyy репозиторий как подкаталогенный Zzz и добавить все XXXИстория изменений в Yyy.

Структура папки раньше:

XXX
 |- .git
 |- (project files)
YYY
 |- .git
 |- (project files)

Структура папки после:

YYY
 |- .git  <-- This now contains the change history from XXX
 |-  ZZZ  <-- This was originally XXX
      |- (project files)
 |-  (project files)

Можно ли это сделать, или я должен прибегнуть к использованию подмодулей?

Это было полезно?

Решение

Наверное, самым простым способом было бы вытащить XXX внести в ветвь в Yyy а затем объедините его в Мастер:

В Yyy:

git remote add other /path/to/XXX
git fetch other
git checkout -b ZZZ other/master
mkdir ZZZ
git mv stuff ZZZ/stuff                      # repeat as necessary for each file/dir
git commit -m "Moved stuff to ZZZ"
git checkout master                
git merge ZZZ --allow-unrelated-histories   # should add ZZZ/ to master
git commit
git remote rm other
git branch -d ZZZ                           # to get rid of the extra branch before pushing
git push                                    # if you have a remote, that is

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

Примечание. Поскольку это было первоначально написано в 2009 году, GIT добавил слияние поддерева, упомянутое в ответе ниже. Я бы, вероятно, использовал бы этот метод сегодня, хотя, конечно, этот метод все еще работает.

Другие советы

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

git remote add XXX_remote <path-or-url-to-XXX-repo>
git fetch XXX_remote
git merge -s ours --no-commit --allow-unrelated-histories XXX_remote/master
git read-tree --prefix=ZZZ/ -u XXX_remote/master
git commit -m "Imported XXX as a subtree."

Вы можете отслеживать изменения вверх по течению, как:

git pull -s subtree XXX_remote master

GIT сами выясняет, где есть корни, прежде чем делать слияние, поэтому вам не нужно указывать префикс на последующих слияниях.

GIT версии до 2.9: Вам не нужно проходить --allow-unrelated-histories вариант на git merge.

Метод в другом ответе, который использует read-tree и пропускает merge -s ours Шаг фактически ничем не отличается от копирования файлов с помощью CP и совершения результата.

Первоначальный источник был из Статья помощи GitHub ".

git-subtree это сценарий, разработанный для именно этого использования случая объединения нескольких репозиториев в один при сохранении истории (и/или разделения истории подделев, хотя это, по -видимому, не имеет отношения к этому вопросу). Распределен как часть дерева git Поскольку релиз 1.7.11.

Чтобы объединить репозиторий <repo> в ревизии <rev> как подкаталог <prefix>, использовать git subtree add следующим образом:

git subtree add -P <prefix> <repo> <rev>

Git-Subtree реализует Стратегия слияния поддерево более удобно для пользователя.

Для вашего случая, внутри репозитория yyy, вы будете бежать:

git subtree add -P ZZZ /path/to/XXX.git master

В самом репозитории GIT есть известный случай, который в совокупности известен в сообществе GIT как »самое крутое слияние"(После темы линии Линуса Торвальдс, используемого в электронном письме в списке рассылки GIT, который описывает это слияние). В этом случае gitk GIT GUI, который теперь является частью собственного GIT, на самом деле был отдельным проектом. Линусу удалось объединить этот репозиторий в репозиторий GIT таким образом, чтобы

  • Он появляется в репозитории GIT, как если бы он всегда был разработан как часть GIT,
  • Вся история остается нетронутой и
  • Его все еще можно разрабатывать независимо в своем старом хранилище, с изменениями просто git pullредакция

Электронная почта содержит шаги, необходимые для воспроизведения, но это не для слабонервных: во-первых, Линус написал Git, так что он, вероятно, знает немного больше об этом, чем о вас, и во -вторых, это было почти 5 лет назад, и GIT улучшился значительно С тех пор, возможно, теперь это намного проще.

В частности, я думаю, в настоящее время в этом конкретном случае можно использовать подмодуль GITK.

Простой способ сделать это-использовать GIT Format-Patch.

Предположим, что у нас есть 2 репозитории GIT Фу а также бар.

Фу содержит:

  • foo.txt
  • .git

бар содержит:

  • bar.txt
  • .git

И мы хотим в конечном итоге Фу содержащий бар История и эти файлы:

  • foo.txt
  • .git
  • foobar/bar.txt

Итак, чтобы сделать это:

 1. create a temporary directory eg PATH_YOU_WANT/patch-bar
 2. go in bar directory
 3. git format-patch --root HEAD --no-stat -o PATH_YOU_WANT/patch-bar --src-prefix=a/foobar/ --dst-prefix=b/foobar/
 4. go in foo directory
 5. git am PATH_YOU_WANT/patch-bar/*

И если мы хотим переписать все сообщения от бара, например, на Linux:

git filter-branch --msg-filter 'sed "1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD

Это добавит [bar] в начале каждого сообщения о коммите.

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

В вашем исходном хранилищах разделить подпапку в новую ветку

git subtree split --prefix=<source-path-to-merge> -b subtree-split-result

В вашем пункте назначения слияние в отделении сплит -результата

git remote add merge-source-repo <path-to-your-source-repository>
git fetch merge-source-repo
git merge -s ours --no-commit merge-source-repo/subtree-split-result
git read-tree --prefix=<destination-path-to-merge-into> -u merge-source-repo/subtree-split-result

Проверьте свои изменения и совершите

git status
git commit

Не забудь

Убрать, удалив subtree-split-result ответвляться

git branch -D subtree-split-result

Удалите удаленную дистанционную дистанцию, добавленную для получения данных из исходного репо.

git remote rm merge-source-repo

Эта функция будет клонировать удаленное репо в локальный репо, после того, как слияние всех коммитов будет сохранено, git log Будет показывать оригинальные коммиты и правильные пути:

function git-add-repo
{
    repo="$1"
    dir="$(echo "$2" | sed 's/\/$//')"
    path="$(pwd)"

    tmp="$(mktemp -d)"
    remote="$(echo "$tmp" | sed 's/\///g'| sed 's/\./_/g')"

    git clone "$repo" "$tmp"
    cd "$tmp"

    git filter-branch --index-filter '
        git ls-files -s |
        sed "s,\t,&'"$dir"'/," |
        GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
        mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
    ' HEAD

    cd "$path"
    git remote add -f "$remote" "file://$tmp/.git"
    git pull "$remote/master"
    git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
    git remote remove "$remote"
    rm -rf "$tmp"
}

Как использовать:

cd current/package
git-add-repo https://github.com/example/example dir/to/save

Если внесите небольшие изменения, вы можете даже переместить файлы/указатели объединенного репо в разные пути, например:

repo="https://github.com/example/example"
path="$(pwd)"

tmp="$(mktemp -d)"
remote="$(echo "$tmp" | sed 's/\///g' | sed 's/\./_/g')"

git clone "$repo" "$tmp"
cd "$tmp"

GIT_ADD_STORED=""

function git-mv-store
{
    from="$(echo "$1" | sed 's/\./\\./')"
    to="$(echo "$2" | sed 's/\./\\./')"

    GIT_ADD_STORED+='s,\t'"$from"',\t'"$to"',;'
}

# NOTICE! This paths used for example! Use yours instead!
git-mv-store 'public/index.php' 'public/admin.php'
git-mv-store 'public/data' 'public/x/_data'
git-mv-store 'public/.htaccess' '.htaccess'
git-mv-store 'core/config' 'config/config'
git-mv-store 'core/defines.php' 'defines/defines.php'
git-mv-store 'README.md' 'doc/README.md'
git-mv-store '.gitignore' 'unneeded/.gitignore'

git filter-branch --index-filter '
    git ls-files -s |
    sed "'"$GIT_ADD_STORED"'" |
    GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
    mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
' HEAD

GIT_ADD_STORED=""

cd "$path"
git remote add -f "$remote" "file://$tmp/.git"
git pull "$remote/master"
git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
git remote remove "$remote"
rm -rf "$tmp"

Уведомления
Пути заменяют через sed, поэтому убедитесь, что он двинулся на правильных путях после слияния.
А --allow-unrelated-histories Параметр существует только после GIT> = 2,9.

Добавление еще одного ответа, так как я думаю, что это немного проще. Притяжение repo_dest осуществляется в репо_ат_импорт, а затем url-set-upstream: repo_dest Master завершен.

Этот метод сработал для меня, импортируя несколько меньших репо в более крупных.

Как импортировать: repo1_to_import to Repo_dest

# checkout your repo1_to_import if you don't have it already 
git clone url:repo1_to_import repo1_to_import
cd repo1_to_import

# now. pull all of repo_dest
git pull url:repo_dest
ls 
git status # shows Your branch is ahead of 'origin/master' by xx commits.
# now push to repo_dest
git push --set-upstream url:repo_dest master

# repeat for other repositories you want to import

Переименовать или перемещать файлы и указать в нужную позицию в оригинальном репо, прежде чем сделать импорт. например

cd repo1_to_import
mkdir topDir
git add topDir
git mv this that and the other topDir/
git commit -m"move things into topDir in preparation for exporting into new repo"
# now do the pull and push to import

Метод, описанный по следующей ссылке, вдохновил этот ответ. Мне понравилось, так как это казалось более простым. Но будьте осторожны! Есть драконы! https://help.github.com/articles/importing-an-external-git-repository git push --mirror url:repo_dest Подталкивает вашу местную историю репо и государство в удаленное (URL: Repo_Dest). Но это удаляет старую историю и состояние отдаленного. Веселье заходит! : -E

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

ALL_COMMITS=$(git log --reverse --pretty=format:%H -- ZZZ | tr '\n' ' ')

Это дает вам разделенную пространство список всех коммитов, которые влияют на файлы, которые я хотел импортировать (ZZZ) в обратном порядке (вам, возможно, придется добавить, чтобы захватить переименование). Затем я вошел в целевой репозиторий (yyy), добавил другой репозиторий (xxx) в качестве удаленного, сделал из него и, наконец,:

git cherry-pick $ALL_COMMITS

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

Я был в ситуации, когда искал -s theirs Но, конечно, этой стратегии не существует. Моя история заключалась в том, что я разделил проект на GitHub, и теперь по какой -то причине мой местный master не мог быть объединен с upstream/master Хотя я не внес локальных изменений в этой ветви. (На самом деле не знаю, что там произошло - я думаю, вверх по течению сделал несколько грязных толкателей за кулисами, может быть?)

Что я закончил, так это

# as per https://help.github.com/articles/syncing-a-fork/
git fetch upstream
git checkout master
git merge upstream/master
....
# Lots of conflicts, ended up just abandonging this approach
git reset --hard   # Ditch failed merge
git checkout upstream/master
# Now in detached state
git branch -d master # !
git checkout -b master   # create new master from upstream/master

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

Видеть Основной пример в эта статья и рассмотрим такое отображение по репозиториям:

  • A <-> YYY,
  • B <-> XXX

После всей деятельности, описанной в этой главе (после слияния), удалите ветвь B-master:

$ git branch -d B-master

Затем протолкните изменения.

Меня устраивает.

Я могу предложить другое решение (альтернатива git-submodules) для вашей проблемы - инструмент GIL (GIT Links)

Это позволяет описывать и управлять сложными зависимостями репозиториев GIT.

Также это обеспечивает решение для git рекурсивные подмодули проблема зависимости.

Считайте, что у вас есть следующие зависимости проекта:Образец графика зависимости репозитория GIT

Тогда вы можете определить .gitlinks Файл с репозиториями Описание Описания:

# Projects
CppBenchmark CppBenchmark https://github.com/chronoxor/CppBenchmark.git master
CppCommon CppCommon https://github.com/chronoxor/CppCommon.git master
CppLogging CppLogging https://github.com/chronoxor/CppLogging.git master

# Modules
Catch2 modules/Catch2 https://github.com/catchorg/Catch2.git master
cpp-optparse modules/cpp-optparse https://github.com/weisslj/cpp-optparse.git master
fmt modules/fmt https://github.com/fmtlib/fmt.git master
HdrHistogram modules/HdrHistogram https://github.com/HdrHistogram/HdrHistogram_c.git master
zlib modules/zlib https://github.com/madler/zlib.git master

# Scripts
build scripts/build https://github.com/chronoxor/CppBuildScripts.git master
cmake scripts/cmake https://github.com/chronoxor/CppCMakeScripts.git master

Каждая строка описывает git link в следующем формате:

  1. Уникальное имя репозитория
  2. Относительный путь репозитория (начинается с пути .gitlinks файла)
  3. Репозиторий GIT, который будет использоваться в филиале репозитория команды GIT Clone для оформления заказа
  4. Пустая линия или линия, начатые с #, не проанализированы (рассматриваются как комментарий).

Наконец, вы должны обновить свой репозиторий пример корневого образца:

# Clone and link all git links dependencies from .gitlinks file
gil clone
gil link

# The same result with a single command
gil update

В результате вы клонируете все необходимые проекты и связываете их друг с другом должным образом.

Если вы хотите совершить все изменения в некоторых репозиториях со всеми изменениями в репозиториях, связанных с детьми, вы можете сделать это с помощью одной команды:

gil commit -a -m "Some big update"

Тянуть, push Commands работает аналогичным образом:

gil pull
gil push

Инструмент GIL (GIT Links) поддерживает следующие команды:

usage: gil command arguments
Supported commands:
    help - show this help
    context - command will show the current git link context of the current directory
    clone - clone all repositories that are missed in the current context
    link - link all repositories that are missed in the current context
    update - clone and link in a single operation
    pull - pull all repositories in the current directory
    push - push all repositories in the current directory
    commit - commit all repositories in the current directory

Больше о git рекурсивные подмодули проблема зависимости.

Я не знаю простого способа сделать это. Вы могли бы сделать это:

  1. Используйте GIT Filter-Tranch, чтобы добавить супер-ректорацию ZZZ в репозиторий XXX
  2. Толкнуть новую ветку в репозиторий Yyy
  3. Объедините толканную ветку с помощью багажника Yyy.

Я могу отредактировать с деталями, если это звучит привлекательно.

Я думаю, что вы можете сделать это, используя «git mv» и «git ull».

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

Во -первых, переименовать структуру XXX, чтобы соответствовать тому, как вы хотите, чтобы она выглядела, когда она находится в пределах Yyy:

cd XXX
mkdir tmp
git mv ZZZ tmp/ZZZ
git mv tmp ZZZ

Теперь ххх выглядит так:

XXX
 |- ZZZ
     |- ZZZ

Теперь используйте «git ourt», чтобы получить изменения в течение:

cd ../YYY
git pull ../XXX

Теперь ты выглядит так:

YYY
 |- ZZZ
     |- ZZZ
 |- (other folders that already were in YYY)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top