考虑以下场景:
我在自己的Git repo中开发了一个小型实验项目a。现在它已经成熟了,我希望A成为更大的项目B的一部分,它有自己的大型存储库。现在我想添加A作为B的子目录。
我如何将A和B合并,而不丢失任何一方的历史?
如果你想合并
1 2 3 4 5 | cd path/to/project-b git remote add project-a path/to/project-a git fetch project-a --tags git merge --allow-unrelated-histories project-a/master # or whichever branch you want to merge git remote remove project-a |
摘自:git合并不同的存储库?
这个方法对我很有效,它更短,在我看来更干净。
注意:
更新:根据@jstadler的建议添加
这里有两个可能的解决方案:
子
要么将存储库A复制到较大的项目B的单独目录中,要么(也许更好)将存储库A复制到项目B的子目录中,然后使用git子模块使该存储库成为存储库B的子模块。
对于松耦合存储库,这是一个很好的解决方案,其中存储库a中的开发将继续进行,而开发的主要部分是在a中单独的独立开发。请参阅Git Wiki上的SubmoduleSupport和GitSubmoduleTutorial页面。
子树合并
您可以使用子树合并策略将存储库A合并到项目B的子目录中。这是由Markus Prinz在子树合并和You中描述的。
1 2 3 4 5 | 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需要选择
或者您也可以使用apenwarr (Avery Pennarun)的git子树工具(GitHub上的repository),比如他在博客文章中宣布了git子模块的一个新替代方案:git子树。
我认为在您的情况下(A是更大的项目B的一部分),正确的解决方案应该是使用子树合并。
可以很容易地将另一个存储库的单个分支放在保留其历史记录的子目录下。例如:
1 | git subtree add --prefix=rails git://github.com/rails/rails.git master |
这将显示为一个提交,其中Rails主分支的所有文件都被添加到"Rails"目录中。然而,提交的标题包含对旧历史树的引用:
Add 'rails/' from commit
其中
1 2 | git log <rev> git blame <rev> -- README.md |
注意,您不能从这里看到目录前缀,因为这是一个完整的实际旧分支。您应该像对待一个普通的文件移动提交一样对待它:当到达它时,您将需要一个额外的跳转。
1 2 3 4 5 | # 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自制程序)。但是除了git之外,您可能还必须自己安装它。
如果希望单独维护项目,则子模块方法很好。然而,如果您真的想将两个项目合并到同一个存储库中,那么您还有更多的工作要做。
第一件事是使用
然后,您应该能够做如下事情:
1 2 | git remote add projb [wherever] git pull projb |
进一步搜索表明,在将
例如,如果
1 | git log projectA |
只列出一个提交:合并。合并项目的提交针对不同的路径,因此它们不会出现。
格雷格?休吉尔(Greg Hewgill)的答案最为接近,不过他并没有具体说明如何重写路径。
解决方案出奇地简单。
(1)在一个,
1 2 3 4 5 6 7 8 | 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 |
注意:这将重写历史记录,因此如果您打算继续使用这个repo A,您可能想首先克隆(复制)它的一次性副本。
然后在B中,运行
1 | git pull path/to/A |
瞧!您在b中有一个
在我的例子中,我需要两个子目录,
如果两个储存库都有相同的文件类型(例如两个Rails储存库用于不同的项目),您可以将辅助储存库的数据提取到当前储存库:
1 | git fetch git://repository.url/repo.git master:branch_name |
然后将其合并到当前存储库:
1 | git merge --allow-unrelated-histories branch_name |
如果Git版本小于2.9,则删除
在此之后,可能会发生冲突。您可以使用
记住要完成合并:
1 | git commit |
在使用merge时,我不断丢失历史记录,所以我最终使用了rebase,因为在我的例子中,这两个存储库足够不同,不会在每次提交时都合并:
1 2 3 4 5 6 7 | 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 |
=>解决冲突,然后继续,需要多少次…
1 | git rebase --continue |
这样做将导致一个项目拥有来自projA的所有提交,然后是来自projB的提交
在我的例子中,我有一个
基本上,我重写了
博士
TL;
1
2
3
4
5
6
7
8
9
$ 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|plugins) 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
1 2 3 4 5 6 7 8 9 | $ 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|plugins) 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 |
长版本
首先,创建一个
现在,导航到
1 | $ git filter-branch -f --tree-filter"zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|plugins) plugins/my-plugin || true)'" -- --all |
现在来解释一下。
如果您运行一个失败的
至于实际的命令,我没有多少运气得到
接下来,我使用
最后,我使用
在我的存储库中,初始提交不包含任何文件,因此
现在,导航到您的
1 | $ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY |
在提交历史中,您现在将有两个不相关的树,您可以很好地使用它们:
1 | $ git log --color --graph --decorate --all |
要合并它们,请使用:
1 | $ git merge my-plugin/master --allow-unrelated-histories |
注意,在pre-2.9.0 Git中,不存在
您不应该有任何合并冲突。如果这样做,可能意味着要么
请确保输入一条解释性的提交消息,以供未来的贡献者了解hackery将如何创建具有两个根的存储库。
您可以使用上面的
可选地,在您合并了所有您想要从
1 | $ git remote remove my-plugin |
现在,您可以安全地删除您更改了历史记录的
测试在Mac OS X El Capitan与
引用:
https://git-scm.com/docs/git-filter-branchhttps://unix.stackexchange.com/questions/6393/how-do-you-move-all-files-including-hidden-from-one-directory-to-anotherhttp://www.refining-linux.org/archives/37/ZSH-Gem-2-Extended-globbing-and-expansion/从Git repo清除文件失败,无法创建新的备份git,所有分支上的过滤器分支几天来我一直在尝试做同样的事情,我使用的是git 2.7.2。子树不保存历史。
如果不再使用旧项目,可以使用此方法。
我建议你先在B分行工作。
以下是没有分支的步骤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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中记录任何文件,您将获得完整的历史记录
1 | git log --follow A/<file> |
这篇文章帮助我做到了这一点:
Merging Two Git Repositories Into One Repository Without Losing File History
我在这里收集了许多关于stack&c;OverFlow等的信息,并设法编写了一个脚本来解决这个问题。
需要注意的是,它只考虑每个存储库的"开发"分支,并将其合并到一个全新存储库中的单独目录中。
标记和其他分支被忽略——这可能不是您想要的。
该脚本甚至处理特性分支和标记——在新项目中重新命名它们,以便您知道它们来自何处。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | #!/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 ## [email protected] ## ## Version: 0.3.2 ## Created: 2018-02-05 ## ################################################################################ # # disallow using undefined variables shopt -s -o nounset # Script variables declare SCRIPT_NAME="${0##*/}" declare SCRIPT_DIR="$(cd ${0%/*} ; pwd)" declare ROOT_DIR="$PWD" IFS=$' ' # Detect proper usage if ["$#" -ne"2" ] ; then echo -e"ERROR: Usage: $0 <new_project> <my_repo_urls.lst>" exit 1 fi ## Script variables PROJECT_NAME="${1}" PROJECT_PATH="${ROOT_DIR}/${PROJECT_NAME}" TIMESTAMP="$(date +%s)" LOG_FILE="${ROOT_DIR}/${PROJECT_NAME}_merge.${TIMESTAMP}.log" REPO_FILE="${2}" REPO_URL_FILE="${ROOT_DIR}/${REPO_FILE}" # Script functions function failed() { echo -e"ERROR: Merging of projects failed:" echo -e"ERROR: Merging of projects failed:">>${LOG_FILE} 2>&1 echo -e"$1" exit 1 } function commit_merge() { current_branch="$(git symbolic-ref HEAD 2>/dev/null)" if [[ ! -f".git/MERGE_HEAD" ]] ; then echo -e"INFO: No commit required." echo -e"INFO: No commit required.">>${LOG_FILE} 2>&1 else echo -e"INFO: Committing ${sub_project}..." echo -e"INFO: Committing ${sub_project}...">>${LOG_FILE} 2>&1 if ! git commit -m"[Project] Merged branch '$1' of ${sub_project}">>${LOG_FILE} 2>&1 ; then failed"Failed to commit merge of branch '$1' of ${sub_project} into ${current_branch}" fi fi } # 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: Logging to ${LOG_FILE}" echo -e"INFO: Creating new git repository ${PROJECT_NAME}..." echo -e"INFO: Creating new git repository ${PROJECT_NAME}...">>${LOG_FILE} 2>&1 echo -e"====================================================" echo -e"====================================================">>${LOG_FILE} 2>&1 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 the branches of this project echo -e"INFO: Merging projects into new repository..." echo -e"INFO: Merging projects into new repository...">>${LOG_FILE} 2>&1 echo -e"====================================================" echo -e"====================================================">>${LOG_FILE} 2>&1 for url in $(cat ${REPO_URL_FILE}) ; do if [["${url:0:1}" == '#' ]] ; then continue fi # extract the name of this project export sub_project=${url##*/} sub_project=${sub_project%*.git} echo -e"INFO: Project ${sub_project}" echo -e"INFO: Project ${sub_project}">>${LOG_FILE} 2>&1 echo -e"----------------------------------------------------" echo -e"----------------------------------------------------">>${LOG_FILE} 2>&1 # Fetch the project echo -e"INFO: Fetching ${sub_project}..." echo -e"INFO: Fetching ${sub_project}...">>${LOG_FILE} 2>&1 git remote add"${sub_project}""${url}" if ! git fetch --tags --quiet ${sub_project} >>${LOG_FILE} 2>&1 ; then failed"Failed to fetch project ${sub_project}" fi # add remote branches echo -e"INFO: Creating local branches for ${sub_project}..." echo -e"INFO: Creating local branches for ${sub_project}...">>${LOG_FILE} 2>&1 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}..." echo -e"INFO: Creating branch ${branch_name}...">>${LOG_FILE} 2>&1 # create and checkout new merge branch off of master if ! git checkout -b"${sub_project}/${branch_name}" master >>${LOG_FILE} 2>&1 ; then failed"Failed preparing ${branch_name}" ; fi if ! git reset --hard ; then failed"Failed preparing ${branch_name}">>${LOG_FILE} 2>&1 ; fi if ! git clean -d --force ; then failed"Failed preparing ${branch_name}">>${LOG_FILE} 2>&1 ; fi # Merge the project echo -e"INFO: Merging ${sub_project}..." echo -e"INFO: Merging ${sub_project}...">>${LOG_FILE} 2>&1 if ! git merge --allow-unrelated-histories --no-commit"remotes/${sub_project}/${branch_name}">>${LOG_FILE} 2>&1 ; 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." echo -e"WARN: Not moving files in branch ${branch_name} of ${sub_project} as already only one root level.">>${LOG_FILE} 2>&1 else echo -e"INFO: Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..." echo -e"INFO: Moving files in branch ${branch_name} of ${sub_project} so we have a single directory...">>${LOG_FILE} 2>&1 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">>${LOG_FILE} 2>&1 ; then failed"sub_project ${sub_project} is missing master branch!" fi # copy remote tags echo -e"INFO: Copying tags for ${sub_project}..." echo -e"INFO: Copying tags for ${sub_project}...">>${LOG_FILE} 2>&1 while read tag ; do tag_ref=$(echo $tag | tr"""\t" | cut -f 1) tag_name_unfixed=$(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_unfixed%%^*}" tag_new_name="${sub_project}/${tag_name}" echo -e"INFO: Copying tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}..." echo -e"INFO: Copying tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}...">>${LOG_FILE} 2>&1 if ! git tag"${tag_new_name}""${tag_ref}">>${LOG_FILE} 2>&1 ; then echo -e"WARN: Could not copy tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}" echo -e"WARN: Could not copy tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}">>${LOG_FILE} 2>&1 fi done < <(git ls-remote --tags --refs ${sub_project}) # Remove the remote to the old project echo -e"INFO: Removing remote ${sub_project}..." echo -e"INFO: Removing remote ${sub_project}...">>${LOG_FILE} 2>&1 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"INFO: Merging projects master branches into new repository...">>${LOG_FILE} 2>&1 echo -e"====================================================" echo -e"====================================================">>${LOG_FILE} 2>&1 for url in $(cat ${REPO_URL_FILE}) ; do if [[ ${url:0:1} == '#' ]] ; then continue fi # extract the name of this project export sub_project=${url##*/} sub_project=${sub_project%*.git} echo -e"INFO: Merging ${sub_project}..." echo -e"INFO: Merging ${sub_project}...">>${LOG_FILE} 2>&1 if ! git merge --allow-unrelated-histories --no-commit"${sub_project}/master">>${LOG_FILE} 2>&1 ; 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 -e"INFO: Done.">>${LOG_FILE} 2>&1 echo exit 0 |
您也可以从http://paste.ubuntu.com/11732805获得它
首先创建一个文件与每个存储库的URL,例如:
1 2 3 | [email protected]:eitchnet/ch.eitchnet.parent.git [email protected]:eitchnet/ch.eitchnet.utils.git [email protected]:eitchnet/ch.eitchnet.privilege.git |
然后调用脚本,给出项目的名称和脚本的路径:
1 | ./mergeGitRepositories.sh eitchnet_test eitchnet.lst |
脚本本身有很多注释,可以解释它的功能。
我知道那是很久以后的事了,但是我对我在这里找到的其他答案不满意,所以我写了这个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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 |
如果您试图将两个存储库简单地粘合在一起,则使用子模块和子树合并是错误的工具,因为它们不能保存所有的文件历史记录(正如人们在其他答案中指出的那样)。看这里的答案,找到简单而正确的方法。
如果您想将repo B中的分支中的文件放到repo a的子树中并保存历史,请继续阅读。(在下面的例子中,我假设我们希望repo B的主分支合并到repo A的主分支中。)
在回购A中,首先执行以下步骤使回购B可用:
1 2 | git remote add B ../B # Add repo B as a new remote. git fetch B |
现在我们在repo a中创建了一个全新的分支(只有一个commit),我们称之为
1 2 3 4 5 6 | 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))" |
说明:checkout命令的
现在,我们将在新创建的
1 2 | git checkout -b b B/master git rebase -s recursive -Xsubtree=path/to/b-files/ new_b_root |
现在,我们将我们的
1 2 3 | git checkout master git merge --allow-unrelated-histories --no-commit b git commit -m 'Merge repo B into repo A.' |
最后,您可以删除
1 2 | git remote remove B git branch -D new_b_root b |
最终的图表结构如下:
我遇到过类似的挑战,但在我的例子中,我们在repo a中开发了一个版本的代码库,然后将其克隆到一个新的repo, repo B,用于产品的新版本。在修复了repo A中的一些bug后,我们需要将更改FI到repo b中。
向指向repo a的repo B添加远程(git远程添加…)绘制当前分支(我们没有使用master进行bug修复)(git绘制remoteForRepoA bugFixBranch)推动合并到github工作待遇:)
将a a合并到B中:
1)在项目A中
1 | git fast-export --all --date-order > /tmp/ProjectAExport |
2)在项目B中
1 2 | git checkout -b projectA git fast-import --force < /tmp/ProjectAExport |
在这个分支中,执行所有需要执行的操作并提交它们。
C)然后回到大师和经典的融合之间的两个分支:
1 2 | git checkout master git merge projectA |
合并2回购
1 2 3 4 5 6 7 8 | 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 |
类似于@Smar,但使用文件系统路径,设置在主要和次要:
1 2 3 4 5 | PRIMARY=~/Code/project1 SECONDARY=~/Code/project2 cd $PRIMARY git remote add test $SECONDARY && git fetch test git merge test/master |
然后手动合并。
(改编自Anar Manafov的文章)
当您想在一次提交中合并三个或更多的项目时,按照其他答案(
现在,用以下内容创建
1 | <commit-id of master> <list of commit ids of all parents> |
运行
现在,您的上一个提交关联了正确的父类。
给定命令是我建议的最佳解决方案。
1 | git subtree add --prefix=MY_PROJECT git://github.com/project/my_project.git master |
我想把一个小项目移到一个大项目的子目录中。由于我的小项目没有很多提交,所以我使用了
这感觉比滤网树枝要干净得多,也不那么可怕。当然,它可能并不适用于所有情况。
该函数将远程repo克隆到本地repo目录,合并所有提交后保存,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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" } |
如何使用:
1 2 | cd current/package git-add-repo https://github.com/example/example dir/to/save |
如果做一些小小的改变,你甚至可以把合并后的回购文件/dirs移动到不同的路径,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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" |
通知路径通过
我稍微手动地合并项目,这允许我避免处理合并冲突。
首先,按照您希望的方式从其他项目复制文件。
1 2 | cp -R myotherproject newdirectory git add newdirectory |
历史上的下一站
1 | git fetch path_or_url_to_other_repo |
告诉git在最后获取的东西的历史中合并
1 | echo 'FETCH_HEAD' > .git/MERGE_HEAD |
现在按照您通常的方式提交
1 | git commit |