un-submodule一个git子模块

我如何un-submodule一个Git子模块(把所有的代码回到核心)?

至于如何“应该”我,如在“最佳程序”…

如果您只想将子模块代码放入主存储库,则只需要删除子模块并将其重新添加到主库中即可:

 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" 

如果您还想保留子模块的历史logging,则可以执行一个小技巧:将子模块“合并”到主存储库中,以便结果与以前相同,只是子模块文件现在处于主存储库。

在主模块中,您需要执行以下操作:

 # 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带来任何问题。

在第二个解决scheme中,您将拥有很大的优势,您仍然可以运行git blame或gitlogin最初位于子模块中的文件。 实际上你在这里做的是重命名一个仓库内的许多文件,git应该自动检测这个。 如果你仍然有问题的git日志,尝试一些选项( – 关注,-M,-C)哪些做更好的重命名/复制检测。

由于git 1.8.5(2013年11月 )( 没有保留子模块的历史 ):

 mv yoursubmodule yoursubmodule_tmp git submodule deinit yourSubmodule git rm yourSubmodule mv yoursubmodule_tmp yoursubmodule git add yoursubmodule 

这将会:

  • 注销和卸载 (即删除子模块的内容 )( deinit ,因此 mv ),
  • 为你清理.gitmodulesrm ),
  • 并删除父代码库( rm )索引中代表子模块SHA1的特殊条目 。

一旦子模块的移除完成( deinitgit rm ),您可以将文件夹重命名为原始名称,并将其作为常规文件夹添加到git repo中。

注意:如果子模块是由旧的Git(<1.8)创build的,则可能需要删除子模块本身内的嵌套.git文件夹,如Simon East


如果您需要保留子模块的历史logging,请参阅jsears的回答 ,它使用git filter-branch

我们碰巧发现,我们为2个项目创build了2个仓库,这些项目如此耦合以至于没有任何意义,所以我们合并了它们。

我将展示如何合并主分支,然后我将解释如何将这个延伸到你得到的每一个分支,希望它能帮助你。

如果你有子模块的工作,并且你想把它转换成一个目录,你可以这样做:

 git clone project_uri project_name 

在这里我们做一个干净的克隆工作。 对于这个过程你不需要初始化或更新子模块,所以只需跳过它。

 cd project_name vim .gitmodules 

用您最喜欢的编辑器(或Vim)编辑.gitmodules以删除您计划replace的子模块。 你需要删除的行应该是这样的:

 [submodule "lib/asi-http-request"] path = lib/asi-http-request url = https://github.com/pokeb/asi-http-request.git 

保存文件后,

 git rm --cached directory_of_submodule git commit -am "Removed submodule_name as submodule" rm -rf directory_of_submodule 

这里我们完全删除子模块的关系,所以我们可以创build其他的repo到项目中。

 git remote add -f submodule_origin submodule_uri git fetch submodel_origin/master 

在这里,我们获取要合并的子模块库。

 git merge -s ours --no-commit submodule_origin/master 

在这里,我们开始2个仓库的合并操作,但是在提交之前停止。

 git read-tree --prefix=directory_of_submodule/ -u submodule_origin/master 

在这里,我们将子模块中的master的内容发送到前缀目录名前的目录

 git commit -am "submodule_name is now part of main project" 

在这里,我们完成了对合并中的更改进行提交的过程。

完成后,您可以推送,并重新开始任何其他分支进行合并,只需签出您将收到更改的分支中的分支,并更改您在合并和读取树操作中引入的分支。

我创build了一个脚本,将一个子模块转换为一个简单的目录,同时保留所有的文件历史logging。 它不会受到其他解决scheme所遭受的git log --follow <file>问题的困扰。 这也是一个非常简单的单行调用,为您完成所有的工作。 G'luck。

它build立在LucasJenß的出色工作上,在他的博客文章“ 将子模块整合到父库中 ”中进行了介绍,但自动执行整个过程并清理了其他几个angular落案例。

最新的代码将在https://github.com/jeremysears/scripts/blob/master/bin/git-submodule-rewrite上的github上的bug修复,但为了正确的stackoverflow答案协议,我已经包括整体解决scheme如下&#x3002;

用法:

 $ git-submodule-rewrite <submodule-name> 

混帐子模块重写:

 #!/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 '.' '\n' | head -n 4)) GIT_VERSION=$(git version) GIT_VERSION=$(printf "%03d%03d%03d%03d" $(echo "${GIT_VERSION#git version}" | tr '.' '\n' | head -n 4)) echo -e "${GIT_VERSION}\n${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 
  1. git rm --cached the_submodule_path
  2. .submodules文件中删除子模块部分
  3. 做一个提交“删除子模块xyz”
  4. git add the_submodule_path
  5. 另一个提交“添加了xyz的代码库”

我还没find更简单的方法。 你可以通过git commit -a来压缩3-5到一个步骤 – 一个味道的问题。

这里有很多答案,但他们都似乎过于复杂,可能不会做你想做的。 我相信大多数人都想保持自己的历史。

对于这个例子,主要的回购将是git@site.com:main/main.git和子模块回购将是git@site.com:main/child.git 。 这假定子模块位于父回购的根目录中。 根据需要调整说明。

首先克隆父回购和删除旧的子模块。

 git clone git@site.com:main/main.git git submodule deinit child git rm child git add --all git commit -m "remove child submodule" 

现在我们将上游的子回购添加到主回购。

 git remote add upstream git@site.com:main/child.git git fetch upstream git checkout -b merge-prep upstream/master 

下一步假定您要将merge-prep分支上的文件移动到子模块所在的位置,尽pipe您可以通过更改文件path轻松更改位置。

 mkdir child 

将.git文件夹以外的所有文件夹和文件移动到子文件夹中。

 git add --all git commit -m "merge prep" 

现在,您可以简单地将您的文件合并回主分支。

 git checkout master git merge merge-prep # --allow-unrelated-histories merge-prep flag may be required 

在运行git push之前, git push查看并确保一切都看起来不错

现在你必须记住的一件事是,git log不会默认跟随移动的文件,但通过运行git log --follow filename可以看到你的文件的完整历史logging。

这个我find的最好的答案在这里:

http://x3ro.de/2013/09/01/Integrating-a-submodule-into-the-parent-repository.html

本文很好地解释了这个过程。

什么时候

 git rm [-r] --cached submodule_path 

回报

 fatal: pathspec 'emr/normalizers/' did not match any files 

上下文:我在我的子模块文件夹中做了rm -r .git* ,然后才意识到它们需要在刚join的主项目中去掉子模块。 去掉一些但不是全部的子模块时,我得到了上述错误。 无论如何,我通过运行来固定它们(当然,在rm -r .git*

 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" 

请注意,这不会保留历史logging。

这里有一个稍微改进的版本(恕我直言)当前最热门的答案:

在一个单独的目录(使错误更容易清理,再试一次)检查顶部回购和subrepo。

 git clone ../main_repo main.tmp git clone ../main_repo/sub_repo sub.tmp 

首先编辑subrepo将所有文件移动到所需的子目录中

 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" 

记下头部

 SUBREPO_HEAD=`git reflog | awk '{ print $1; exit; }'` 

现在从主要的回购删除subrepo

 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" 

最后,合并它们

 git fetch ../sub.tmp git merge $SUBREPO_HEAD 

并做了! 安全,没有任何魔法。

基于VonC的回答 ,我已经创build了一个简单的bash脚本。 最后add必须使用通配符,否则它将撤销子模块本身的前一个rm 。 添加子模块目录的内容很重要,不要在add命令中命名目录本身。

在一个名为git-integrate-submodule的文件中:

 #!/usr/bin/env bash mv "$1" "${1}_" git submodule deinit "$1" git rm "$1" mv "${1}_" "$1" git add "$1/**" 

我发现从子模块中(也?)获取本地提交数据会更方便,否则我将会丢失它们。 (不能推他们,因为我没有访问该远程)。 所以我添加submodule / .git作为remote_origin2,提取它提交并从该分支合并。 不知道是否我仍然需要远程的子模块作为起源,因为我还不熟悉git。