Import a git repository with all its history into an existing git repository
我有两个Git存储库,我希望将它们合并在一起,而不会丢失它们的提交历史记录。我试过了:
1 2 3 4 5 6 | cd firstRepo git remote add other path/to/otherRepo git fetch other git checkout -b otherRepoBranch other/master echo"`git rev-list otherRepoBranch | tail -n 1` `git rev-list master | head -n 1`">> .git/info/grafts git rebase otherRepoBranch master |
现在,当我查看提交历史记录时,一切看起来都很好,但是我的存储库中只有来自Otherrepo的文件。
有什么想法吗?
干杯,默林
我认为Git存储库是无关的?就像它们是单独项目的不同存储库一样,您希望将它们合并在一起形成一个组合的存储库?如果是这样,那么以下参考可能会有所帮助:
组合多个Git存储库。
合并两个不相关的存储库。
这篇决定性的文章讨论了开头一段中的简单案例——并将更可能的案例作为其主要主题:如何使用子树合并策略
两个存储库的提交历史记录都会保留。
这是我的版本——基于上面提到的文章……
1 2 3 4 5 6 7 8 9 | git remote add temp staging_path/(reponame) git fetch temp git fetch --tags temp ## optional -- may pull in additional history for remote in $(git branch -r | grep temp/ ) ; do git branch --no-track imported_$(basename $remote) $remote ; done ## create local branches prefixed with 'imported_' git remote rm temp ## optional -- assumes you no longer plan to use the source repo git merge -s ours --no-commit imported_master ## mysterious"merge strategy" git read-tree -u --prefix=(reponame)/ imported_master ## move to sub-folder git commit |
我知道现在已经很晚了,但是对于仍在寻找方法的人来说,我已经收集了很多关于stackoverflow等的信息,并设法将一个脚本放在一起,从而为我解决了这个问题。
这个脚本甚至可以处理特性分支和标记——在新项目中对它们进行重命名,以便您知道它们来自哪里。
我知道现在已经很晚了,但是对于仍在寻找方法的人来说,我已经收集了很多关于stackoverflow等的信息,并设法将一个脚本放在一起,从而为我解决了这个问题。
这个脚本甚至可以处理特性分支和标记——在新项目中对它们进行重命名,以便您知道它们来自哪里。
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 | #!/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.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创建一个文件,例如:
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 |
脚本本身有很多注释,应该解释它的作用。
编辑后用更高级的脚本替换脚本。
在最简单的情况下,如果您希望合并后两个存储库中的所有文件都能使用git merge:
1 2 3 4 5 | cd firstRepo git remote add other path/to/otherRepo git fetch other git checkout -b merged git merge --allow-unrelated-histories other/master |
1 2 | git checkout master git merge otherRepoBranch |
最后被遗忘了。
这对于在主分支上应用钢筋是必要的。
这些看起来都很难理解…为什么不只是:
1 2 3 4 5 6 7 8 9 | # Make a bare clone of the external repo to a local directory without a working directory git clone --bare https://githost.org/extuser/repo.git # now push all history and repo to new repo cd repo.git git push --mirror https://github.com/ghuser/repo.git # Push mirror to new GitHub repo cd .. rm -rf repo.git # Remove temporary local repo |