如何取消子模块化git子模块(将所有代码重新放入核心)?
就像“应该”一样,如“最佳程序”......
答案 0 :(得分:479)
如果您只想将子模块代码放入主存储库,则只需删除子模块并将文件重新添加到主存储库中:
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"
如果你还想保留子模块的历史记录,你可以做一个小技巧:将子模块“合并”到主存储库中,以便结果与以前一样,除了子模块文件是现在在主存储库中。
在主模块中,您需要执行以下操作:
# 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 blame或git log。实际上你在这里做的是重命名一个存储库中的许多文件,git应该自动检测这个。如果您仍然遇到git log问题,请尝试一些选项( - follow,-M,-C),这些选项可以更好地重命名/复制检测。
答案 1 :(得分:63)
自git 1.8.5 (Nov 2013)(不保留子模块的历史记录):
mv yoursubmodule yoursubmodule_tmp
git submodule deinit yourSubmodule
git rm yourSubmodule
mv yoursubmodule_tmp yoursubmodule
git add yoursubmodule
那将:
deinit
,因此mv
首先 >),.gitmodules
(rm
),rm
)索引中代表该子模块SHA1的special entry。完成子模块的移除后(deinit
和git rm
),您可以将文件夹重命名为其原始名称,并将其作为常规文件夹添加到git repo中。
注意:如果子模块是由旧的Git(< 1.8)创建的,则可能需要删除子模块内的嵌套.git
文件夹,commented为Simon East
如果您需要保留子模块的历史记录,请参阅使用git filter-branch
的{{3}}的jsears。
答案 2 :(得分:52)
我创建了一个脚本,它将子模块转换为简单目录,同时保留所有文件历史记录。它不会受到其他解决方案所遇到的git log --follow <file>
问题的影响。它也是一个非常简单的单行调用,可以为您完成所有工作。 G'luck。
它建立在LucasJenß的优秀作品之上,在他的博客文章“Integrating a submodule into the parent repository”中有所描述,但它可以自动化整个过程并清理其他一些极端案例。
最新的代码将在https://github.com/jeremysears/scripts/blob/master/bin/git-submodule-rewrite的github上使用错误修正来维护,但为了正确的stackoverflow应答协议,我在下面全部包含了解决方案。
用法:
$ git-submodule-rewrite <submodule-name>
<强> GIT-子模块重写:强>
#!/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
答案 3 :(得分:20)
git rm --cached the_submodule_path
.gitmodules
文件中删除子模块部分,或者如果它是唯一的子模块,则删除该文件。git add the_submodule_path
我没有找到任何更简单的方法。您可以通过git commit -a
- 品味来压缩3-5到一步。
答案 4 :(得分:14)
这里有很多答案,但所有这些答案似乎过于复杂,可能不会做你想要的。我相信大多数人都想保留他们的历史。
对于此示例,主仓库将为git@site.com:main/main.git
,子模块仓库将为git@site.com:main/child.git
。这假定子模块位于父repo的根目录中。根据需要调整说明。
首先克隆父repo并删除旧的子模块。
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分支上的文件移动到与上面的子模块相同的位置,尽管您可以通过更改文件路径轻松更改位置。
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 log默认不会跟踪移动的文件,但是通过运行git log --follow filename
你可以看到文件的完整历史记录。
答案 5 :(得分:12)
我们碰巧为2个项目创建了两个存储库,它们是如此耦合,将它们分开是没有任何意义的,所以我们将它们合并。
我将展示如何在每个分支中合并主分支,然后我将解释如何将其扩展到您获得的每个分支,希望它对您有所帮助。
如果您的子模块正常工作,并且您想将其转换为适当的目录,您可以这样做:
git clone project_uri project_name
这里我们做一个干净的克隆工作。对于此过程,您无需初始化或更新子模块,因此请跳过它。
cd project_name
vim .gitmodules
使用您喜欢的编辑器(或Vim)编辑.gitmodules
以删除您要替换的子模块。您需要删除的行应如下所示:
[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
这里我们完全删除了子模块关系,这样我们就可以创建其他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"
这里我们完成了在合并中提交更改的过程。
完成此操作后,您可以推送,并重新启动任何其他分支进行合并,只需检查您将收到更改的存储库中的分支,并更改您在合并和读取树操作中引入的分支。
答案 6 :(得分:6)
我找到的最佳答案就在这里:
http://x3ro.de/2013/09/01/Integrating-a-submodule-into-the-parent-repository.html
本文非常好地解释了该程序。
答案 7 :(得分:3)
何时
git rm [-r] --cached submodule_path
返回
fatal: pathspec 'emr/normalizers/' did not match any files
上下文:我在我的子模块文件夹中做了rm -r .git*
,然后才意识到他们需要在刚刚添加它们的主项目中进行去子模块化。在解调一些时,我得到了上述错误,但不是全部。无论如何,我通过跑步来修复它们(当然,之后是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"
请注意,这不会保留历史记录。
答案 8 :(得分:3)
这是当前最佳答案的略微改进版本(恕我直言):
在另一个目录中(为了使错误更容易清理并再次尝试),请查看顶部回购和子回报。
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"
记下HEAD
SUBREPO_HEAD=`git reflog | awk '{ print $1; exit; }'`
现在从主仓库
中删除子代码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
完成了!安全而没有任何魔力。
答案 9 :(得分:2)
基于VonC's answer,我创建了一个简单的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/**"
答案 10 :(得分:0)
我发现(同样?)从子模块获取本地提交数据更方便,否则我会松掉它们。 (无法推送它们,因为我无法访问该遥控器)。所以我添加了submodule / .git作为remote_origin2,提取它并从该分支合并。 不确定我是否还需要子模块远程作为原点,因为我还不熟悉git。
答案 11 :(得分:0)
这是我发现的最好最简单的东西。
在子模块存储库中,您要从HEAD合并到主存储库:
git checkout -b "mergeMe"
mkdir "foo/bar/myLib/"
(与主存储库中文件所在的路径相同)git mv * "foo/bar/myLib/"
(全部进入路径)git commit -m "ready to merge into main"
在删除子模块并清除路径“ foo / bar / myLib”后返回主仓库:
git merge --allow-unrelated-histories SubmoduleOriginRemote/mergeMe
动臂完成
保存的历史
不用担心
请注意,这与其他答案几乎相同。但这假设您拥有子模块存储库。同样,这也使得获取子模块将来的上游更改变得容易。