Как объединить два репозитория Git?
рассмотрим следующий сценарий:
Я разработал небольшой экспериментальный проект A в своем собственном РЕПО Git. Теперь он созрел, и я хотел бы быть частью более крупного проекта B, который имеет свой собственный большой репозиторий. Теперь я хотел бы добавить A в качестве подкаталога B.
Как мне объединить A в B, не теряя историю ни с одной стороны?
21 ответ:
одна ветвь другого репозитория может быть легко помещена в подкаталог с сохранением его истории. Например:
git subtree add --prefix=rails git://github.com/rails/rails.git masterэто будет отображаться как одна фиксация, где все файлы Rails master branch добавляются в каталог "rails". Однако заголовок фиксации содержит ссылку на старое дерево истории:
добавить 'rails/' из commit
<rev>здесь
<rev>является хэшем фиксации SHA-1. Вы все еще можете увидеть историю, виноваты некоторые изменения.git log <rev> git blame <rev> -- README.mdобратите внимание, что вы не можете видеть префикс каталога отсюда, так как это фактическая старая ветвь осталась нетронутой. Вы должны относиться к этому как к обычной фиксации перемещения файла: вам понадобится дополнительный прыжок при его достижении.
# finishes with all files added at once commit git log rails/README.md # then continue from original tree git log <rev> -- README.mdесть более сложные решения, такие как выполнение этого вручную или переписывание истории, как описано в других ответах.
команда git-subtree является частью официального git-contrib, некоторые менеджеры пакетов устанавливают ее по умолчанию (OS X Homebrew). Но вам придется установить его самостоятельно в дополнение к Git.
если вы хотите объединить
project-aнаproject-b:cd path/to/project-b git remote add project-a path/to/project-a git fetch project-a git merge --allow-unrelated-histories project-a/master # or whichever branch you want to merge git remote remove project-aвзято из: git объединить различные репозитории?
этот метод работал довольно хорошо для меня, это короче и, на мой взгляд, намного чище.
Примечание: The
--allow-unrelated-historiesпараметр существует только с git >= 2.9. Смотрите git-git merge Documentation / --allow-unrelated-histories
вот два возможных решения:
подмодулей
либо скопируйте репозиторий A в отдельный каталог в более крупном проекте B, либо (возможно, лучше) клонируйте репозиторий A в подкаталог в проекте B. затем используйте git подмодуль чтобы сделать этот репозитарий субмодуль из репозитория B.
это хорошее решение для слабо связанных репозиториев, где разработка в репозитории a продолжается, а основные часть разработки-это отдельная самостоятельная разработка в A. см. Также SubmoduleSupport и GitSubmoduleTutorial страницы на Git Wiki.
поддерево слияния
вы можете объединить репозиторий A в подкаталог проекта B с помощью поддерево слияния стратегии. Это описано в слияние поддеревьев и вы Маркус принц.
git remote add -f Bproject /path/to/B git merge -s ours --allow-unrelated-histories --no-commit Bproject/master git read-tree --prefix=dir-B/ -u Bproject/master git commit -m "Merge B project as our subdirectory" git pull -s subtree Bproject master(вариант
--allow-unrelated-historiesis необходимо для Git >= 2.9.0.)или вы можете использовать git поддерево (репозиторий на GitHub) от apenwarr (Avery Pennarun), объявленный, например, в своем блоге новая альтернатива подмодулям Git: поддерево git.
Я думаю, что в вашем случае (A должен быть частью более крупного проекта B) правильным решением было бы использовать поддерево слияния.
подход подмодуля хорош, если вы хотите поддерживать проект отдельно. Однако, если вы действительно хотите объединить оба проекта в один репозиторий, то у вас есть немного больше работы.
первое, что было бы использовать
git filter-branchчтобы переписать имена всего во втором репозитории, чтобы быть в подкаталоге, где вы хотите, чтобы они оказались. Так что вместоfoo.c,bar.html, ты быprojb/foo.cиprojb/bar.html.тогда, вы должны быть может сделать что-то вроде следующего:
git remote add projb [wherever] git pull projbThe
git pullбудетgit fetchзатемgit merge. Не должно быть никаких конфликтов, если репозиторий вы тянете еще не было!--9-- каталог>.дальнейший поиск указывает на то, что нечто подобное было сделано для слияния
gitkнаgit. Junio C Hamano пишет об этом здесь:http://www.mail-archive.com/git@vger.kernel.org/msg03395.html
git-subtreeприятно, но это, вероятно, не тот, который вы хотите.например, если
projectAэто каталог, созданный в B, послеgit subtree,git log projectAсписки единственный commit: слияние. Коммиты из объединенного проекта предназначены для разных путей, поэтому они не отображаются.
Грег Hewgill это ближе всего, хотя это на самом деле не сказать, как переписать пути.
в решение на удивление простое.
(1) В A,
PREFIX=projectA #adjust this git filter-branch --index-filter ' git ls-files -s | sed "s,\t,&'"$PREFIX"'/," | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE ' HEADПримечание: это переписывает историю, так что если вы намерены продолжать использовать это РЕПО A, вы можете клонировать (копировать) одноразовую копию его в первую очередь.
(2) затем в B запустите
git pull path/to/Aвуаля! У вас есть
projectAкаталог в B. Если вы запуститеgit log projectA, вы увидите все коммиты от A.
в моем случае, я хотел два поддиректориями,
projectAиprojectB. В этом случае я также сделал шаг (1) к B.
если оба репозитория имеют одинаковые файлы (например, два репозитория Rails для разных проектов), вы можете получить данные вторичного репозитория в свой текущий репозиторий:
git fetch git://repository.url/repo.git master:branch_nameи затем объединить его в текущий репозиторий:
git merge --allow-unrelated-histories branch_nameесли ваша версия Git меньше 2.9, удалите
--allow-unrelated-histories.после этого могут возникать конфликты. Вы можете решить их, например, с помощью
git mergetool.kdiff3может использоваться только с клавиатурой, поэтому 5 конфликтных файлов занимает при чтении кода всего несколько минут.не забудьте завершить слияние:
git commit
Я продолжал терять историю при использовании слияния, поэтому я использовал rebase, так как в моем случае два репозитория достаточно разные, чтобы не сливаться при каждом фиксации:
git clone git@gitorious/projA.git projA git clone git@gitorious/projB.git projB cd projB git remote add projA ../projA/ git fetch projA git rebase projA/master HEAD=> разрешать конфликты, а затем продолжить, столько раз, сколько необходимо...
git rebase --continueэто приводит к тому, что один проект имеет все коммиты из projA, за которыми следуют коммиты из projB
в моем случае, у меня было
my-pluginхранилище иmain-projectрепозиторий, и я хотел сделать вид, чтоmy-pluginвсегда были разработаны вpluginsподдиректориюmain-project.в основном, я переписал историю
my-pluginрепозиторий так что оказалось все разработки проходили вplugins/my-pluginподдиректории. Затем я добавил историю развитияmy-pluginнаmain-projectистория, и объединил два дерева вместе. Так как не было уже присутствует вmain-projectрепозиторий, это было тривиальное слияние без конфликтов. Получившийся репозиторий содержал всю историю от обоих оригинальных проектов, и имел два корня.TL; DR
$ cp -R my-plugin my-plugin-dirty $ cd my-plugin-dirty $ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|my-plugin) plugins/my-plugin || true)'" -- --all $ cd ../main-project $ git checkout master $ git remote add --fetch my-plugin ../my-plugin-dirty $ git merge my-plugin/master --allow-unrelated-histories $ cd .. $ rm -rf my-plugin-dirtyтекст
во-первых, создайте копию
my-pluginрепозиторий, потому что мы собираемся переписать историю этого репозитория.Теперь перейдите к корню
my-pluginрепозиторий, проверьте свою основную ветку (возможноmaster), и выполните следующую команду. Конечно, вы должны заменитьmy-pluginиpluginsкаковы бы ни были ваши настоящие имена.$ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|my-plugin) plugins/my-plugin || true)'" -- --allтеперь для объяснения.
git filter-branch --tree-filter (...) HEADработает(...)команда на каждом коммите, который доступен изHEAD. Обратите внимание, что это работает непосредственно с данными, хранящимися для каждой фиксации, поэтому нам не нужно беспокоиться о понятиях "рабочий каталог", "индекс", "промежуточный" и т. д.если вы используете что не удается, он оставит после себя некоторые файлы в
.gitкаталог и в следующий раз, когда вы попробуетеfilter-branchон будет жаловаться на это, если вы не поставить доfilter-branch.что касается фактической команды, мне не очень повезло получить
bashделать то, что я хотел, поэтому я используюzsh -cсделатьzshвыполнить команду. Сначала я установилextended_globопция, которая позволяет^(...)синтаксис , а также , которым позволяет мне выбрать dotfiles (например,.gitignore) С Глоб (^(...)).далее, я использую для создания
pluginsиplugins/my-pluginв то же время.наконец, я использую
zshфункция "отрицательный глобус"^(.git|my-plugin)для сопоставления всех файлов в корневом каталоге репозитория, кроме.gitи вновь созданный . (Исключая.gitвозможно, здесь нет необходимости, но попытка переместить каталог в себя является ошибка.)в моем репозитории начальная фиксация не включала никаких файлов, поэтому
mvкоманда возвратила ошибку при начальной фиксации (так как ничего не было доступно для перемещения). Поэтому я добавил|| true, так чтоgit filter-branchне прерывать.The говорит
filter-branchпереписать историю для все ветки в репозитории, а лишние--надо сказатьgitчтобы интерпретировать его как часть списка опций для ветвей переписать, а не как вариантfilter-branchсам по себе.Теперь перейдите к вашему
main-projectрепозиторий и проверьте любую ветку, в которую вы хотите слиться. Добавьте локальную копиюmy-pluginрепозиторий (с измененной историей) как удаленный отmain-projectС:$ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORYтеперь у вас будет два несвязанных дерева в истории фиксации, которые вы можете хорошо визуализировать с помощью:
$ git log --color --graph --decorate --allобъединить их, использование:
$ git merge my-plugin/master --allow-unrelated-historiesобратите внимание, что в pre-2.9.0 Git, не существует. Если вы используете одну из этих версий, просто опустите опцию: сообщение об ошибке, что
--allow-unrelated-historiesпредотвращает была и добавил в 2.9.0.вы не должны иметь никаких конфликтов слияния. Если вы это сделаете, это, вероятно, означает, что либо
filter-branchкоманда не работала правильно или уже была наmain-project.убедитесь, что чтобы ввести пояснительное сообщение о фиксации для любых будущих участников, задающихся вопросом, Что такое хакерство, чтобы сделать репозиторий с двумя корнями.
вы можете визуализировать новый граф фиксации, который должен иметь два корневых фиксации, используя выше . Обратите внимание, что только
masterветка будет объединена. Это означает, что если у вас есть важная работа над другимиmy-pluginветви, которые вы хотите объединить вmain-projectдерево, вы должны воздержаться от удаленияmy-pluginудаленный, пока вы не сделали эти сливает. Если вы этого не сделаете, то коммиты из этих ветвей все равно будут вmain-projectрепозиторий, но некоторые из них будут недоступны и восприимчивы к возможной сборке мусора. (Кроме того, вам придется ссылаться на них по SHA, потому что удаление удаленного удаляет его ветви удаленного отслеживания.)необязательно, после того, как вы объединили все, что вы хотите сохранить от
my-plugin, вы можете удалитьmy-pluginremote использование:$ git remote remove my-pluginтеперь вы можете безопасно удалить копию
my-pluginрепозитория, историю которого вы изменили. В моем случае, я также добавил уведомление об устаревании к реальномуmy-pluginрепозиторий после завершения слияния и толкнул.
протестировано на Mac OS X El Capitan с
git --version 2.9.0иzsh --version 5.2. Ваш пробег может отличаться.ссылки:
- https://git-scm.com/docs/git-filter-branch
- https://unix.stackexchange.com/questions/6393/how-do-you-move-all-files-including-hidden-from-one-directory-to-another
- http://www.refining-linux.org/archives/37/ZSH-Gem-2-Extended-globbing-and-expansion/
- не удалось удалить файл из репозитория Git, не удалось создать новая резервная копия
- git, фильтр-ветка на всех ветках
Я пытался сделать то же самое в течение нескольких дней, я использую git 2.7.2. Поддерево не сохраняет историю.
Вы можете использовать этот метод, если вы не будете снова использовать старый проект.
Я бы предположил, что вы ветку в первую очередь, и работы в филиале.
вот шаги без ветвления:
cd B # You are going to merge A into B, so first move all of B's files into a sub dir mkdir B # Move all files to B, till there is nothing in the dir but .git and B git mv <files> B git add . git commit -m "Moving content of project B in preparation for merge from A" # Now merge A into B git remote add -f A <A repo url> git merge A/<branch> mkdir A # move all the files into subdir A, excluding .git git mv <files> A git commit -m "Moved A into subdir" # Move B's files back to root git mv B/* ./ rm -rf B git commit -m "Reset B to original state" git pushЕсли вы теперь регистрируете любой из файлов в subdir A, вы получите полную историю
git log --follow A/<file>Это был пост, который помогите мне сделать это:
Я знаю, что это долго после факта, но я не был доволен другими ответами, которые я нашел здесь, поэтому я написал это:
me=$(basename ) TMP=$(mktemp -d /tmp/$me.XXXXXXXX) echo echo "building new repo in $TMP" echo sleep 1 set -e cd $TMP mkdir new-repo cd new-repo git init cd .. x=0 while [ -n "" ]; do repo=""; shift git clone "$repo" dirname=$(basename $repo | sed -e 's/\s/-/g') if [[ $dirname =~ ^git:.*\.git$ ]]; then dirname=$(echo $dirname | sed s/.git$//) fi cd $dirname git remote rm origin git filter-branch --tree-filter \ "(mkdir -p $dirname; find . -maxdepth 1 ! -name . ! -name .git ! -name $dirname -exec mv {} $dirname/ \;)" cd .. cd new-repo git pull --no-commit ../$dirname [ $x -gt 0 ] && git commit -m "merge made by $me" cd .. x=$(( x + 1 )) done
я собрал здесь много информации о переполнении стека и т. д., и вам удалось собрать сценарий, который решает проблему для меня.
оговорка заключается в том, что он учитывает только ветвь "разработка" каждого репозитория и объединяет ее в отдельный каталог в совершенно новом репозитории.
теги и другие ветви игнорируются - это может быть не то, что вы хотите.
скрипт даже обрабатывает ветви функций и теги-переименование их в новом проекте, чтобы вы знали, откуда они пришли.
#!/bin/bash # ################################################################################ ## Script to merge multiple git repositories into a new repository ## - The new repository will contain a folder for every merged repository ## - The script adds remotes for every project and then merges in every branch ## and tag. These are renamed to have the origin project name as a prefix ## ## Usage: mergeGitRepositories.sh <new_project> <my_repo_urls.lst> ## - where <new_project> is the name of the new project to create ## - and <my_repo_urls.lst> is a file containing the URLs to the repositories ## which are to be merged on separate lines. ## ## Author: Robert von Burg ## eitch@eitchnet.ch ## ## Version: 0.2.0 ## Created: 2015-06-17 ## ################################################################################ # # Disallow using undefined variables shopt -s -o nounset # Script variables declare SCRIPT_NAME="${0##*/}" declare SCRIPT_DIR="$(cd ${0%/*} ; pwd)" declare ROOT_DIR="$PWD" # Detect proper usage if [ "$#" -ne "2" ] ; then echo -e "ERROR: Usage: <new_project> <my_repo_urls.lst>" exit 1 fi # Script functions function failed() { echo -e "ERROR: Merging of projects failed:" echo -e "" exit 1 } function commit_merge() { current_branch="$(git symbolic-ref HEAD 2>/dev/null)" CHANGES=$(git status | grep "working directory clean") MERGING=$(git status | grep "merging") if [[ "$CHANGES" != "" ]] && [[ "$MERGING" == "" ]] ; then echo -e "INFO: No commit required." else echo -e "INFO: Committing ${sub_project}..." if ! git commit --quiet -m "[Project] Merged branch '' of ${sub_project}" ; then failed "Failed to commit merge of branch '' of ${sub_project} into ${current_branch}" fi fi } ## Script variables PROJECT_NAME="" PROJECT_PATH="${ROOT_DIR}/${PROJECT_NAME}" REPO_FILE="" REPO_URL_FILE="${ROOT_DIR}/${REPO_FILE}" # Make sure the REPO_URL_FILE exists if [ ! -e "${REPO_URL_FILE}" ] ; then echo -e "ERROR: Repo file ${REPO_URL_FILE} does not exist!" exit 1 fi # Make sure the required directories don't exist if [ -e "${PROJECT_PATH}" ] ; then echo -e "ERROR: Project ${PROJECT_NAME} already exists!" exit 1 fi # Create the new project echo -e "INFO: Creating new git repository ${PROJECT_NAME}..." echo -e "====================================================" cd ${ROOT_DIR} mkdir ${PROJECT_NAME} cd ${PROJECT_NAME} git init echo "Initial Commit" > initial_commit # Since this is a new repository we need to have at least one commit # thus were we create temporary file, but we delete it again. # Deleting it guarantees we don't have conflicts later when merging git add initial_commit git commit --quiet -m "[Project] Initial Master Repo Commit" git rm --quiet initial_commit git commit --quiet -m "[Project] Initial Master Repo Commit" echo # Merge all projects into th branches of this project echo -e "INFO: Merging projects into new repository..." echo -e "====================================================" for url in $(cat ${REPO_URL_FILE}) ; do # Extract the name of this project export sub_project=${url##*/} sub_project=${sub_project%*.git} echo -e "INFO: Project ${sub_project}" echo -e "----------------------------------------------------" # Fetch the project echo -e "INFO: Fetching ${sub_project}..." git remote add "${sub_project}" "${url}" if ! git fetch --no-tags --quiet ${sub_project} 2>/dev/null ; then failed "Failed to fetch project ${sub_project}" fi # Add remote branches echo -e "INFO: Creating local branches for ${sub_project}..." while read branch ; do branch_ref=$(echo $branch | tr " " "\t" | cut -f 1) branch_name=$(echo $branch | tr " " "\t" | cut -f 2 | cut -d / -f 3-) echo -e "INFO: Creating branch ${branch_name}..." # Create and checkout new merge branch off of master git checkout --quiet -b "${sub_project}/${branch_name}" master git reset --hard --quiet git clean -d --force --quiet # Merge the project echo -e "INFO: Merging ${sub_project}..." if ! git merge --quiet --no-commit "remotes/${sub_project}/${branch_name}" 2>/dev/null ; then failed "Failed to merge branch 'remotes/${sub_project}/${branch_name}' from ${sub_project}" fi # And now see if we need to commit (maybe there was a merge) commit_merge "${sub_project}/${branch_name}" # Relocate projects files into own directory if [ "$(ls)" == "${sub_project}" ] ; then echo -e "WARN: Not moving files in branch ${branch_name} of ${sub_project} as already only one root level." else echo -e "INFO: Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..." mkdir ${sub_project} for f in $(ls -a) ; do if [[ "$f" == "${sub_project}" ]] || [[ "$f" == "." ]] || [[ "$f" == ".." ]] ; then continue fi git mv -k "$f" "${sub_project}/" done # Commit the moving if ! git commit --quiet -m "[Project] Move ${sub_project} files into sub directory" ; then failed "Failed to commit moving of ${sub_project} files into sub directory" fi fi echo done < <(git ls-remote --heads ${sub_project}) # Checkout master of sub probject if ! git checkout "${sub_project}/master" 2>/dev/null ; then failed "sub_project ${sub_project} is missing master branch!" fi # Copy remote tags echo -e "INFO: Copying tags for ${sub_project}..." while read tag ; do tag_ref=$(echo $tag | tr " " "\t" | cut -f 1) tag_name=$(echo $tag | tr " " "\t" | cut -f 2 | cut -d / -f 3) # hack for broken tag names where they are like 1.2.0^{} instead of just 1.2.0 tag_name="${tag_name%%^*}" tag_new_name="${sub_project}/${tag_name}" echo -e "INFO: Copying tag ${tag_name} to ${tag_new_name} for ref ${tag_ref}..." if ! git tag "${tag_new_name}" "${tag_ref}" 2>/dev/null ; then echo -e "WARN: Could not copy tag ${tag_name} to ${tag_new_name} for ref ${tag_ref}" fi done < <(git ls-remote --tags ${sub_project}) # Remove the remote to the old project echo -e "INFO: Removing remote ${sub_project}..." git remote rm ${sub_project} echo done # Now merge all project master branches into new master git checkout --quiet master echo -e "INFO: Merging projects master branches into new repository..." echo -e "====================================================" for url in $(cat ${REPO_URL_FILE}) ; do # extract the name of this project export sub_project=${url##*/} sub_project=${sub_project%*.git} echo -e "INFO: Merging ${sub_project}..." if ! git merge --quiet --no-commit "${sub_project}/master" 2>/dev/null ; then failed "Failed to merge branch ${sub_project}/master into master" fi # And now see if we need to commit (maybe there was a merge) commit_merge "${sub_project}/master" echo done # Done cd ${ROOT_DIR} echo -e "INFO: Done." echo exit 0вы также можете получить его от http://paste.ubuntu.com/11732805
сначала создайте файл с URL-адресом для каждого репозитория, например:
git@github.com:eitchnet/ch.eitchnet.parent.git git@github.com:eitchnet/ch.eitchnet.utils.git git@github.com:eitchnet/ch.eitchnet.privilege.gitзатем вызовите скрипт, указав имя проекта и путь к скрипту:
./mergeGitRepositories.sh eitchnet_test eitchnet.lstсам скрипт имеет много комментариев, которые должны объяснить, что он делает.
Если вы пытаетесь просто склеить два репозитория вместе, подмодули и слияния поддеревьев являются неправильным инструментом для использования, потому что они не сохраняют всю историю файлов (как люди отметили в других ответах). Смотрите этот ответ здесь для простого и правильного способа сделать это.
если вы хотите поместить файлы из ветки в репо B В a поддерева РЕПО с и также сохраните историю, продолжайте читать. (В приведенном ниже примере я предполагаю, что мы хотим, чтобы главная ветвь РЕПО B была объединена в главную ветвь РЕПО A.)
в репо A сначала выполните следующие действия, чтобы сделать РЕПО B доступным:
git remote add B ../B # Add repo B as a new remote. git fetch Bтеперь мы создаем совершенно новую ветвь (с только одним фиксацией) в репо A, что мы звоните
new_b_root. Результирующая фиксация будет иметь файлы, которые были зафиксированы в первой фиксации главной ветви repo B, но помещены в подкаталог с именемpath/to/b-files/.git checkout --orphan new_b_root master git rm -rf . # Remove all files. git cherry-pick -n `git rev-list --max-parents=0 B/master` mkdir -p path/to/b-files git mv README path/to/b-files/ git commit --date="$(git log --format='%ai' $(git rev-list --max-parents=0 B/master))"Пояснение: The
--orphanопция для команды checkout проверяет файлы из главной ветви A, но не создает никаких фиксаций. Мы могли бы выбрать любую фиксацию, потому что затем мы все равно очистим все файлы. Затем, не совершая еще (-n), мы выбираем первый коммит от мастера B отделение. (Cherry-pick сохраняет исходное сообщение фиксации, которое прямая проверка, похоже, не делает.) Затем мы создаем поддерево, куда мы хотим поместить все файлы из РЕПО B. Затем мы должны переместить все файлы, которые были введены в cherry-pick в поддерево. В приведенном выше примере, есть только перейти. Затем мы совершаем нашу корневую фиксацию B-repo, и в то же время мы также сохраняем метку времени исходной фиксации.теперь мы создадим новый
B/masterветка поверх вновь созданногоnew_b_root. Мы называем новую веткуb:git checkout -b b B/master git rebase -s recursive -Xsubtree=path/to/b-files/ new_b_rootтеперь мы объединяем наши
bфилиала вA/master:git checkout master git merge --allow-unrelated-histories --no-commit b git commit -m 'Merge repo B into repo A.'наконец, вы можете удалить
Bудаленные и временные ветки:git remote remove B git branch -D new_b_root bокончательный график будет иметь такую структуру:
У меня была аналогичная проблема, но в моем случае мы разработали одну версию кодовой базы в репо A, а затем клонировали ее в новое РЕПО, РЕПО B, для новой версии продукта. После исправления некоторых ошибок в репо A нам нужно было внести изменения в репо B. В итоге мы сделали следующее:
- добавление пульта дистанционного управления в репо B, который указал на репо A (git remote add...)
- потянув текущую ветку (мы не использовали master для исправления ошибок) (git pull remoteForRepoA bugFixBranch)
- нажатие сливается с github
сработала :)
похоже на @Smar, но использует пути файловой системы, установленные в первичном и вторичном:
PRIMARY=~/Code/project1 SECONDARY=~/Code/project2 cd $PRIMARY git remote add test $SECONDARY && git fetch test git merge test/masterпотом вручную сливать.
(взято из сообщение Анара Манафова)
если вы хотите, чтобы объединить три или более проектов в один commit, выполните действия, описанные в других ответах (
remote add -f,merge). Затем (мягкий) сбросьте индекс до старой головы (где слияние не произошло). Добавить все файлы (git add -A) и зафиксировать их (сообщение "объединение проектов A, B, C и D в один проект). Теперь это commit-id мастера.Теперь создайте
.git/info/graftsследующего содержания:<commit-id of master> <list of commit ids of all parents>выполнить
git filter-branch -- head^..head head^2..head head^3..head. Если у вас есть больше, чем три ветки, просто добавьте столькоhead^n..headа у вас есть филиалы. Чтобы обновить теги, добавьте--tag-name-filter cat. Не всегда добавляю, потому что это может привести к перезаписи некоторых коммитов. Подробнее см. man страница фильтра-branch поиск "графты".теперь ваш последний коммит связан с правильными родителями.
чтобы объединить a a в B:
1) в проекте
git fast-export --all --date-order > /tmp/ProjectAExport2) в проекте B
git checkout -b projectA git fast-import --force < /tmp/ProjectAExportв этой ветке выполните все операции, которые вам нужно сделать, и зафиксируйте их.
C) затем вернемся к мастеру и классическому слиянию между двумя ветвями:
git checkout master git merge projectA
слияние 2 РЕПО
git clone ssh://<project-repo> project1 cd project1 git remote add -f project2 project2 git merge --allow-unrelated-histories project2/master git remote rm project2 delete the ref to avoid errors git update-ref -d refs/remotes/project2/master
данная команда является наилучшим решением, которое я предлагаю.
git subtree add --prefix=MY_PROJECT git://github.com/project/my_project.git master
эта функция клонирует удаленное РЕПО в локальный РЕПО dir, после слияния все коммиты будут сохранены,
git logбудут показаны исходные коммиты и правильные пути:function git-add-repo { repo="" dir="$(echo "" | 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если внести небольшие изменения, вы даже можете перемещать файлы / dirs объединенного РЕПО в разные пути, например:
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 "" | sed 's/\./\./')" to="$(echo "" | 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.
Я объединить проекты немного вручную, что позволяет избежать необходимости иметь дело с конфликтами слияния.
во-первых, скопируйте в файлы из другого проекта, как вы хотите их.
cp -R myotherproject newdirectory git add newdirectoryследующая тяга в истории
git fetch path_or_url_to_other_repoскажите git, чтобы слиться в истории последней извлеченной вещи
echo 'FETCH_HEAD' > .git/MERGE_HEADтеперь совершите, однако вы обычно совершаете
git commit
