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
), - 为你清理
.gitmodules
(rm
), - 并删除父代码库(
rm
)索引中代表子模块SHA1的特殊条目 。
一旦子模块的移除完成( deinit
和git 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落案例。
用法:
$ 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
-
git rm --cached the_submodule_path
- 从
.submodules
文件中删除子模块部分 - 做一个提交“删除子模块xyz”
-
git add the_submodule_path
- 另一个提交“添加了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。