un-submodule a git submodule
如何取消Git子模块的子模块(将所有代码放回核心)?
至于"应该"如何,如"最佳程序"…
如果您只想将子模块代码放入主存储库,只需删除子模块并将文件重新添加到主repo中:
1 2 3 4 5 6 | git rm --cached submodule_path # delete reference to submodule HEAD (no trailing slash) git rm .gitmodules # if you have more than one submodules, # you need to edit this file instead of deleting! rm -rf submodule_path/.git # make sure you have backup!! git add submodule_path # will add files instead of commit reference git commit -m"remove submodule" |
如果您还想保留子模块的历史,您可以做一个小技巧:"合并"子模块到主存储库中,这样结果将与以前一样,只是子模块文件现在在主存储库中。
在主模块中,您需要执行以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # Fetch the submodule commits into the main repository git remote add submodule_origin git://url/to/submodule/origin git fetch submodule_origin # Start a fake merge (won't change any files, won't commit anything) git merge -s ours --no-commit submodule_origin/master # Do the same as in the first solution git rm --cached submodule_path # delete reference to submodule HEAD git rm .gitmodules # if you have more than one submodules, # you need to edit this file instead of deleting! rm -rf submodule_path/.git # make sure you have backup!! git add submodule_path # will add files instead of commit reference # Commit and cleanup git commit -m"removed submodule" git remote rm submodule_origin |
结果的存储库看起来有点奇怪:将有多个初始提交。但不会给Git带来任何问题。
在第二个解决方案中,您将有一个很大的优势,即仍然可以在最初位于子模块中的文件上运行git责备或git日志。实际上,您在这里所做的是重命名一个存储库中的许多文件,而Git应该自动检测到这一点。如果Git日志仍然有问题,请尝试一些选项(--follow,-m,-c),它们可以更好地重命名/复制检测。
自Git 1.8.5(2013年11月)起(不保留子模块历史):
1 2 3 4 5 | mv yoursubmodule yoursubmodule_tmp git submodule deinit yourSubmodule git rm yourSubmodule mv yoursubmodule_tmp yoursubmodule git add yoursubmodule |
那将:
- 注销并卸载(即删除)子模块(
deinit 的内容,因此先卸载mv , - 为你清理
.gitmodules (rm ) - 并在母公司回购索引(
rm 中删除表示该子模块sha1的特殊条目。
一旦删除子模块完成(
注意:如果子模块是由一个旧的git(<1.8)创建的,您可能需要删除子模块本身中嵌套的EDOCX1[7]文件夹,正如Simon East所评论的那样。
如果您需要保留子模块的历史记录,请参阅jsears的答案,它使用
我创建了一个脚本,将子模块转换为一个简单的目录,同时保留所有的文件历史记录。它不会受到其他解决方案所面临的
这是建立在卢卡斯·珍出色的作品之上的?在他的博客文章"将子模块集成到父存储库"中进行了描述,但是自动化了整个过程并清理了其他一些角落的案例。
最新的代码将通过在github上的错误修复进行维护,网址为https://github.com/jeremysers/scripts/blob/master/bin/git-submodule-rewrite,但是为了正确的stackoverflow应答协议,我已经在下面完整地包含了解决方案。
用途:
1 | $ git-submodule-rewrite <submodule-name> |
Git子模块重写:
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 | #!/usr/bin/env bash # This script builds on the excellent work by Lucas Jen?, described in his blog # post"Integrating a submodule into the parent repository", but automates the # entire process and cleans up a few other corner cases. # https://x3ro.de/2013/09/01/Integrating-a-submodule-into-the-parent-repository.html function usage(){ echo"Merge a submodule into a repo, retaining file history." echo"Usage: $0 <submodule-name>" echo"" echo"options:" echo" -h, --help Print this message" echo" -v, --verbose Display verbose output" } function abort { echo"$(tput setaf 1)$1$(tput sgr0)" exit 1 } function request_confirmation { read -p"$(tput setaf 4)$1 (y/n) $(tput sgr0)" ["$REPLY" =="y" ] || abort"Aborted!" } function warn() { cat << EOF This script will convert your"${sub}" git submodule into a simple subdirectory in the parent repository while retaining all contents and file history. The script will: * delete the ${sub} submodule configuration from .gitmodules and .git/config and commit it. * rewrite the entire history of the ${sub} submodule so that all paths are prefixed by ${path}. This ensures that git log will correctly follow the original file history. * merge the submodule into its parent repository and commit it. NOTE: This script might completely garble your repository, so PLEASE apply this only to a fresh clone of the repository where it does not matter if the repo is destroyed. It would be wise to keep a backup clone of your repository, so that you can reconstitute it if need be. You have been warned. Use at your own risk. EOF request_confirmation"Do you want to proceed?" } function git_version_lte() { OP_VERSION=$(printf"%03d%03d%03d%03d" $(echo"$1" | tr '.' ' ' | head -n 4)) GIT_VERSION=$(git version) GIT_VERSION=$(printf"%03d%03d%03d%03d" $(echo"${GIT_VERSION#git version}" | tr '.' ' ' | head -n 4)) echo -e"${GIT_VERSION} ${OP_VERSION}" | sort | head -n1 [ ${OP_VERSION} -le ${GIT_VERSION} ] } function main() { warn if ["${verbose}" =="true" ]; then set -x fi # Remove submodule and commit git config -f .gitmodules --remove-section"submodule.${sub}" if git config -f .git/config --get"submodule.${sub}.url"; then git config -f .git/config --remove-section"submodule.${sub}" fi rm -rf"${path}" git add -A . git commit -m"Remove submodule ${sub}" rm -rf".git/modules/${sub}" # Rewrite submodule history local tmpdir="$(mktemp -d -t submodule-rewrite-XXXXXX)" git clone"${url}""${tmpdir}" pushd"${tmpdir}" local tab="$(printf '\t')" local filter="git ls-files -s | sed "s/${tab}/${tab}${path}\//" | GIT_INDEX_FILE=\${GIT_INDEX_FILE}.new git update-index --index-info && mv \${GIT_INDEX_FILE}.new \${GIT_INDEX_FILE}" git filter-branch --index-filter"${filter}" HEAD popd # Merge in rewritten submodule history git remote add"${sub}""${tmpdir}" git fetch"${sub}" if git_version_lte 2.8.4 then # Previous to git 2.9.0 the parameter would yield an error ALLOW_UNRELATED_HISTORIES="" else # From git 2.9.0 this parameter is required ALLOW_UNRELATED_HISTORIES="--allow-unrelated-histories" fi git merge -s ours --no-commit ${ALLOW_UNRELATED_HISTORIES}"${sub}/master" rm -rf tmpdir # Add submodule content git clone"${url}""${path}" rm -rf"${path}/.git" git add"${path}" git commit -m"Merge submodule contents for ${sub}" git config -f .git/config --remove-section"remote.${sub}" set +x echo"$(tput setaf 2)Submodule merge complete. Push changes after review.$(tput sgr0)" } set -euo pipefail declare verbose=false while [ $# -gt 0 ]; do case"$1" in (-h|--help) usage exit 0 ;; (-v|--verbose) verbose=true ;; (*) break ;; esac shift done declare sub="${1:-}" if [ -z"${sub}" ]; then >&2 echo"Error: No submodule specified" usage exit 1 fi shift if [ -n"${1:-}" ]; then >&2 echo"Error: Unknown option: ${1:-}" usage exit 1 fi if ! [ -d".git" ]; then >&2 echo"Error: No git repository found. Must be run from the root of a git repository" usage exit 1 fi declare path="$(git config -f .gitmodules --get"submodule.${sub}.path")" declare url="$(git config -f .gitmodules --get"submodule.${sub}.url")" if [ -z"${path}" ]; then >&2 echo"Error: Submodule not found: ${sub}" usage exit 1 fi if ! [ -d"${path}" ]; then >&2 echo"Error: Submodule path not found: ${path}" usage exit 1 fi main |
我还没找到更容易的方法。你可以通过
这里有很多答案,但所有的答案似乎都过于复杂,很可能无法满足您的需求。我相信大多数人都想保留他们的历史。
在本例中,主回购为
首先克隆父repo并删除旧的子模块。
1 2 3 4 5 | git clone [email protected]:main/main.git git submodule deinit child git rm child git add --all git commit -m"remove child submodule" |
现在我们将把子回购协议添加到主回购协议的上游。
1 2 3 | git remote add upstream [email protected]:main/child.git git fetch upstream git checkout -b merge-prep upstream/master |
下一步假设您希望将合并准备分支上的文件移动到与上面的子模块相同的位置,尽管您可以通过更改文件路径轻松地更改位置。
1 | mkdir child |
将除.git文件夹外的所有文件夹和文件移到子文件夹中。
1 2 | git add --all git commit -m"merge prep" |
现在您可以简单地将文件合并回主分支。
1 2 | git checkout master git merge merge-prep # --allow-unrelated-histories merge-prep flag may be required |
在运行
现在您必须记住的一件事是,Git日志在默认情况下不会跟踪移动的文件,但是通过运行
我们碰巧为两个项目创建了两个存储库,它们是如此地耦合以至于分离它们毫无意义,所以我们合并了它们。
我将首先演示如何合并每个主分支,然后我将解释如何将其扩展到您所拥有的每个分支,希望它对您有所帮助。
如果子模块正常工作,并且要将其转换为适当的目录,可以执行以下操作:
1 | git clone project_uri project_name |
在这里我们做一个干净的克隆工作。对于这个过程,您不需要初始化或更新子模块,所以跳过它。
1 2 | cd project_name vim .gitmodules |
用您最喜欢的编辑器(或vim)编辑
1 2 3 | [submodule"lib/asi-http-request"] path = lib/asi-http-request url = https://github.com/pokeb/asi-http-request.git |
保存文件后,
1 2 3 | git rm --cached directory_of_submodule git commit -am"Removed submodule_name as submodule" rm -rf directory_of_submodule |
在这里,我们完全删除子模块关系,这样我们就可以创建一个将另一个repo带到项目中。
1 2 | git remote add -f submodule_origin submodule_uri git fetch submodel_origin/master |
在这里,我们获取要合并的子模块存储库。
1 | git merge -s ours --no-commit submodule_origin/master |
在这里,我们开始两个存储库的合并操作,但在提交之前停止。
1 | git read-tree --prefix=directory_of_submodule/ -u submodule_origin/master |
在这里,我们将子模块中master的内容发送到它在前缀目录名之前所在的目录。
1 | git commit -am"submodule_name is now part of main project" |
在这里,我们完成了在合并中提交更改的过程。
完成此操作后,您可以推送并重新启动要合并的任何其他分支,只需签出存储库中接收更改的分支,然后更改引入合并和读取树操作的分支。
我找到的最好的答案是:
http://x3ro.de/2013/09/01/integrating-a-submodule-into-the-parent-repository.html
本文很好地解释了这个过程。
下面是当前热门答案的稍微改进版本(imho):
在一个单独的目录中(为了使错误更容易清理并重试),检查顶部回购和子报告。
1 2 | git clone ../main_repo main.tmp git clone ../main_repo/sub_repo sub.tmp |
首先编辑子报告,将所有文件移动到所需的子目录中。
1 2 3 4 | cd sub.tmp mkdir sub_repo_path git mv `ls | grep -v sub_repo_path` sub_repo_path/ git commit -m"Moved entire subrepo into sub_repo_path" |
记下头部
1 | SUBREPO_HEAD=`git reflog | awk '{ print $1; exit; }'` |
现在从主回购中移除子回购
1 2 3 4 5 | cd ../main.tmp rmdir sub_repo_path vi .gitmodules # remove config for submodule git add -A git commit -m"Removed submodule sub_repo_path in preparation for merge" |
最后,合并它们
1 2 | git fetch ../sub.tmp git merge $SUBREPO_HEAD |
完成了!安全,没有任何魔力。
因为什么时候
1 | git rm [-r] --cached submodule_path |
收益率
1 | fatal: pathspec 'emr/normalizers/' did not match any files |
上下文:我在子模块文件夹中执行了
1 2 3 4 5 6 | mv submodule_path submodule_path.temp git add -A . git commit -m"De-submodulization phase 1/2" mv submodule_path.temp submodule_path git add -A . git commit -m"De-submodulization phase 2/2" |
请注意,这并不能保留历史。
基于VONC的答案,我创建了一个简单的bash脚本来完成这个任务。末尾的
在名为
1 2 3 4 5 6 | #!/usr/bin/env bash mv"$1""${1}_" git submodule deinit"$1" git rm"$1" mv"${1}_""$1" git add"$1/**" |
我发现(也是?)更方便从子模块获取本地提交数据,否则我将释放它们。(无法推送它们,因为我无法访问该遥控器)。所以我添加了子模块/.git作为远程的origin2,获取了它的提交并从该分支中合并。不知道我是否还需要子模块远程作为源,因为我还不太熟悉git。