你如何合并两个Git仓库?

考虑以下情况:

我在自己的Git回购中开发了一个小实验项目A. 现在已经成熟了,我希望A成为大型项目B的一部分,它有自己的大型仓库。 我现在想添加A作为B的一个子目录。

如何将A合并到B中,而不会丢失任何一方的历史logging?

另一个存储库的单个分支可以很容易地放置在保存其历史的子目录下。 例如:

git subtree add --prefix=rails git://github.com/rails/rails.git master 

这将作为一个单独的提交,其中Rails主分支的所有文件都被添加到“rails”目录中。 但是,提交的标题包含对旧历史树的引用。

 Add 'rails/' from 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 

还有其他更复杂的解决scheme,如手动执行或重写其他答案中所述的历史logging。

git-subtree命令是官方git-contrib的一部分,一些数据包pipe理器默认安装它(OS X Homebrew)。 但是你可能需要自己安装它,除了git。

如果你想合并project-a -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合并不同的仓库?

这个方法对我来说工作得非常好,而且在我看来,这个方法更简洁。

注意: –allow-unrelated-history参数只存在于git> = 2.9之后。

有两种可能的解决scheme:

子模块

将存储库A复制到大型项目B中的单独目录中,或者(可能更好)将存储库A复制到项目B中的子目录中。然后使用git子模块将此存储库作为存储库B的子模块

对于松散耦合的存储库来说,这是一个很好的解决scheme,在存储库A中的开发仍在继续,而大部分开发是在A中单独独立开发的。另请参阅Git Wiki上的子模块支持和GitSubmoduleTutorial页面。

子树合并

您可以使用子树合并策略将存储库A合并到项目B的子目录中。 这在Markus Prinz的Subtree Merging and You中有描述。

 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 

(git> = 2.9.0需要选项--allow-unrelated-histories允许--allow-unrelated-histories

或者你可以使用apenwarr(Avery Pennarun)的git subtree工具( github上的仓库),例如在他的博客文章中公布git子模块的新select:git subtree 。


我认为你的情况(A是要成为大型项目B的一部分)正确的解决scheme将是使用子树合并

如果要分开维护项目,则子模块方法很好。 但是,如果您真的想将两个项目合并到同一个存储库,那么您还有更多的工作要做。

第一件事是使用git filter-branch将第二个存储库中的所有内容的名称重新写入子目录中,最后让它们结束。 所以,而不是foo.cbar.html ,你会有projb/foo.cprojb/bar.html

那么,你应该能够做到以下几点:

 git remote add projb [wherever] git pull projb 

git pull会执行git fetch然后是git merge 。 如果你所使用的版本库还没有projb/目录,应该没有冲突。

进一步的search表明,类似的事情已经完成了将gitk合并到git 。 Junio C Hamano在这里写道: http : //www.mail-archive.com/git@vger.kernel.org/msg03395.html

git-subtree是不错的,但它可能不是你想要的。

例如,如果projectA是在B中创build的目录,那么在git subtree

 git log projectA 

列出一个提交:合并。 合并项目的提交是不同的path,所以他们不显示。

Greg Hewgill的答案最接近,但实际上并没有说如何重写path。


解决scheme非常简单。

(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 

注意:这会重写历史logging,所以如果您打算继续使用此回购A,您可能需要首先克隆(复制)它的一次性拷贝。

(2)然后在B中运行

 git pull path/to/A 

瞧! 您在B中有一个projectA目录。如果您运行git log projectA ,您将看到来自A的所有提交。


在我的情况下,我想要两个子目录, projectAprojectB 。 在这种情况下,我也对B进行了步骤(1)。

如果两个存储库都有相同types的文件(比如不同项目的两个Rails存储库),用git-fetch

 git fetch git://repository.url/repo.git master:branch_name 

然后将其合并到当前的存储库

 git merge branch_name 

会让你有冲突和“手动”解决这些例如与git-mergetool。 kdiff3只能和键盘一起使用,所以只需要几分钟的时间读取代码就可以得到5个冲突文件。

记得用git-commit完成合并

 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一直在main-projectplugins子目录下main-project

基本上,我重写了my-plugin存储库的历史logging,使得所有的开发都发生在plugins/my-plugin子目录中。 然后,我将my-plugin的开发历史logging添加到main-project历史logging中,并将两棵树合并在一起。 由于main-project存储库中不存在plugins/my-plugin目录,因此这是一个无关紧要的无冲突合并。 最终的存储库包含两个原始项目的所有历史logging,并有两个根。

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 

长版

首先,创build一个my-plugin版本库的副本,因为我们将要重写这个版本库的历史。

现在,导航到my-plugin库的根目录,检查你的主分支(可能是master ),并运行以下命令。 当然,你应该replacemy-pluginplugins无论你的名字是什么。

 $ 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到达的提交上运行(...)命令。 请注意,这个操作直接针对每个提交所存储的数据,所以我们不必担心“工作目录”,“索引”,“分段”等概念。

如果运行失败的filter-branch命令,它会在.git目录下留下一些文件,下一次尝试filter-branch时会抱怨这一点,除非你提供了-f选项给filter-branch

至于实际的命令,我没有太多的运气让bash做我想做的,所以我使用zsh -c来让zsh执行一个命令。 首先,我设置了extended_glob选项,这是mv命令中启用^(...)语法的glob_dots选项,也是glob_dots选项,它允许我使用glob( ^(...) )select点文件(例如.gitignore^(...) )。

接下来,我使用mkdir -p命令同时创buildpluginsplugins/my-plugin

最后,我使用zsh “negative glob”特性^(.git|my-plugin)来匹配仓库根目录下除.git和新创build的my-plugin文件夹以外的所有文件。 (不包括.git在这里可能不需要,但是尝试将一个目录移动到自身是一个错误。)

在我的仓库中,初始提交没有包含任何文件,所以mv命令在初始提交时返回了一个错误(因为没有任何东西可以移动)。 因此,我添加了一个|| true || true所以git filter-branch不会中止。

--all选项告诉filter-branch为存储库中的所有分支重写历史logging,而extra --则是告诉git将其解释为分支要重写的选项列表的一部分,而不是作为选项filter-branch本身。

现在,导航到你的main-project库,并检查出你想要合并到的任何分支。 将my-plugin库的本地副本(修改其历史logging)添加为远程的main-project

 $ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY 

现在,您将在提交历史logging中拥有两个不相关的树,可以很好地使用它们:

 $ git log --color --graph --decorate --all 

要合并它们,请使用:

 $ git merge my-plugin/master --allow-unrelated-histories 

请注意,在2.9.0之前的Git中,– --allow-unrelated-histories选项不存在。 如果您正在使用这些版本之一,则只需省略选项: 在2.9.0 添加了 --allow-unrelated-histories可防止的错误消息

你不应该有任何合并冲突。 如果这样做,可能意味着filter-branch命令无法正常工作,或者main-project已经有了plugins/my-plugin目录。

确保input一个解释性的提交信息,以供将来的贡献者想知道什么hackery正在做一个有两个根的仓库。

您可以使用上面的git log命令可视化新的提交图,该提交图应该有两个根提交。 请注意, 只有master分支将被合并 。 这意味着如果你在其他的my-plugin分支上有重要的工作,你想合并到main-project树中,你应该避免删除my-plugin远程直到你完成了这些合并。 如果你不这样做,那么来自这些分支的提交将仍然在main-project库中,但是有些将不可用,并且容易被最终垃圾收集。 (另外,您将不得不通过SHA来引用它们,因为删除远程会删除其远程跟踪分支。)

可选地,在合并了所有要从my-plugin保留的所有内容后,可以使用以下命令删除my-plugin remote:

 $ git remote remove my-plugin 

现在,您可以安全地删除其历史logging已更改的my-plugin存储库的副本。 在我的情况下,我也在合并完成并推送之后,将一个弃用通知添加到真正的my-plugin存储库中。


使用git --version 2.9.0zsh --version 5.2在Mac OS X El Capitan上testing。 你的旅费可能会改变。

参考文献:

我一直在试图做同样的事情几天,我使用git 2.7.2。 子树不保存历史。

如果您不再使用旧项目,则可以使用此方法。

我build议你先分支B,然后在分支上工作。

这里是没有分支的步骤:

 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 

如果你现在logging子目录A中的任何文件,你将得到完整的历史logging

 git log --follow A/<file> 

这是帮助我这样做的post:

http://saintgimp.org/2013/01/22/merging-two-git-repositories-into-one-repository-without-losing-file-history/

我知道事情已经很久了,但是我对这里发现的其他答案并不满意,于是我写下了这个:

 me=$(basename $0) 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 "$1" ]; do repo="$1"; 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 

如果您试图简单地将两个存储库粘合在一起,则子模块和子树合并是错误的使用工具,因为它们不保留所有文件历史logging(正如人们在其他答案中所指出的那样)。 在这里看到这个答案的简单和正确的方法来做到这一点。

我也遇到了类似的挑战,但就我而言,我们已经开发了一个版本的repo A代码库,然后将其克隆到新版本的产品的新回购库(repo B)中。 修复了A库中的一些bug之后,我们需要将这些修改变成回购B.完成以下工作:

  1. 添加一个远程回购B,指向回购A(git remote add …)
  2. 拉当前分支(我们没有使用主修正错误)(git pull remoteForRepoA bugFixBranch)
  3. 合并到github

做了一个治疗:)

我知道这是相当晚,但对于任何人仍然在寻找一种方式来做到这一点,我已经收集了很多关于StackOverFlow等的信息,并已经设法为我解决问题的脚本。

需要注意的是,它只考虑每个存储库的“开发”分支,并将其合并到全新存储库中的单独目录中。

标签和其他分支被忽略 – 这可能不是你想要的。

该脚本甚至处理function分支和标签 – 重命名他们在新项目,所以你知道他们来自哪里。

 #!/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 contaning the URLs to the respositories ## 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: $0 <new_project> <my_repo_urls.lst>" exit 1 fi # Script functions function failed() { echo -e "ERROR: Merging of projects failed:" echo -e "$1" 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 '$1' of ${sub_project}" ; then failed "Failed to commit merge of branch '$1' of ${sub_project} into ${current_branch}" fi fi } ## Script variables PROJECT_NAME="${1}" PROJECT_PATH="${ROOT_DIR}/${PROJECT_NAME}" REPO_FILE="${2}" 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创build一个文件,例如:

 git@github.com:eitchnet/ch.eitchnet.parent.git git@github.com:eitchnet/ch.eitchnet.utils.git git@github.com:eitchnet/ch.eitchnet.privilege.git 

然后调用脚本给出项目的名称和脚本的path:

 ./mergeGitRepositories.sh eitchnet_test eitchnet.lst 

脚本本身有很多评论,应该解释它的作用。

类似于@Smar,但使用文件系统path,在PRIMARY和SECONDARY中设置:

 PRIMARY=~/Code/project1 SECONDARY=~/Code/project2 cd $PRIMARY git remote add test $SECONDARY && git fetch test git merge test/master 

然后你手动合并。

( 由Anar Manafov改编)

当你想在一次提交中合并三个或更多项目时,按照其他答案( remote add -fmerge )中所述的步骤进行操作。 然后,(软)重置索引到旧头(没有合并发生)。 添加所有文件( git add -A )并提交它们(消息“合并项目A,B,C和D到一个项目中),这就是master的commit-id。

现在,创build包含以下内容的.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 。 如果你有三个以上的分支机构,只需要添加尽可能多的分支机构。 要更新标签,请附加--tag-name-filter cat 。 不要总是添加,因为这可能会导致一些提交的重写。 有关详细信息,请参阅filter分支的手册页 ,search“移植”。

现在,你的最后一次提交有正确的父母关联。

在B中合并A:

1)在项目A中

 git fast-export --all --date-order > /tmp/ProjectAExport 

2)在项目B中

 git checkout -b projectA git fast-import --force < /tmp/ProjectAExport 

在这个分支中,执行所有需要做的操作并提交它们。

C) Then back to the master and a classical merge between the two branches:

 git checkout master git merge projectA 

Merging 2 repos

 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 

Given command is the best possible solution I suggest.

 git subtree add --prefix=MY_PROJECT git://github.com/project/my_project.git master 

This function will clone remote repo into local repo dir, after merging all commits will be saved, git log will be show the original commits and proper paths:

 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 

If make a little changes you can even move files/dirs of merged repo into different paths, for example:

 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" 

Notices
Paths replaces via sed , so make sure it moved in proper paths after merging.
The --allow-unrelated-histories parameter only exists since git >= 2.9.