对Git子模块进行非子模块化,将所有代码带回核心存储库的最佳实践是什么?
当前回答
当
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"
注意,这并不能保存历史。
其他回答
以下是我发现的最好最简单的方法。
在子模块repo中,你想从HEAD合并到main repo:
git checkout -b "mergeMe" mkdir "foo/bar/myLib/"(与你想要在主repo上的文件的路径相同) git mv * "foo/bar/myLib/"(移动所有到路径) Git commit -m“准备合并到main”
在删除子模块并清除路径"foo/bar/myLib"后,回到主repo:
SubmoduleOriginRemote/mergeMe
繁荣做
记录保存
不用担心
注意,这与其他一些答案几乎相同。但这假设你拥有子模块repo。此外,这也使子模块的未来上游更改变得容易。
我已经创建了一个脚本,将子模块转换到一个简单的目录,同时保留所有的文件历史。它不会像其他解决方案那样受到git log——follow <file>问题的困扰。它也是一个非常简单的单行调用,可以为您完成所有工作。G 'luck。
它建立在Lucas Jenß的出色工作之上,在他的博客文章“将子模块集成到父存储库中”中描述了这一点,但是它自动化了整个过程,并清理了一些其他的极端情况。
最新的代码将在github上的https://github.com/jeremysears/scripts/blob/master/bin/git-submodule-rewrite上进行错误修复,但为了适当的stackoverflow回答协议,我在下面包含了完整的解决方案。
用法:
$ git-submodule-rewrite <submodule-name>
git-submodule-rewrite:
#!/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 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日志的问题,尝试一些选项(例如,——follow, -M, -C),它们可以更好地重命名和复制检测。
我找到的最佳答案是:
http://x3ro.de/2013/09/01/Integrating-a-submodule-into-the-parent-repository.html
这篇文章很好地解释了这个过程。
基于VonC的回答,我创建了一个简单的bash脚本。最后的add必须使用通配符,否则它将撤销子模块本身先前的rm。重要的是要添加子模块目录的内容,而不是在add命令中命名目录本身。
在一个名为git- integration -submodule的文件中:
#!/usr/bin/env bash
mv "$1" "${1}_"
git submodule deinit "$1"
git rm "$1"
mv "${1}_" "$1"
git add "$1/**"
推荐文章
- Visual Studio Code无法检测已安装的Git
- 强制LF eol在git的回购和工作副本
- Git:在裸库中更改活动分支的正确方法?
- 删除git中的分支是否会将其从历史记录中删除?
- 防止在GitHub上推送到master ?
- 根据Git,谁是“我们”,谁是“他们”?
- git如何合并后樱桃采摘工作?
- Git搜索单个文件历史记录中的字符串
- Git命令显示所有(轻量级)标签创建日期
- Gitignore并没有忽视文件夹
- 什么时候用。git/info/exclude代替。gitignore来排除文件?
- 如何告诉git忽略个别行,即gitignore为特定行代码
- “git diff——耐心”是什么意思?
- 我如何在GitHub中为其他人的代码做出贡献?
- 签出旧的提交并使其成为新的提交