如何取消子模块 Git 子模块?

发布于 2024-08-11 05:53:08 字数 42 浏览 4 评论 0 原文

取消子模块 Git 子模块、将所有代码带回核心存储库的最佳实践是什么?

What are the best practices for un-submoduling a Git submodule, bringing all the code back into the core repository?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

汐鸠 2024-08-18 05:53:08

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

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 --force submodule_path  # will add files instead of commit reference
                                # --force adds files ignored by .gitignore
git commit -m "remove submodule"

如果您还想保留子模块的历史记录,您可以做一个小操作技巧:将子模块“合并”到主存储库中,这样结果将与以前相同,只是子模块文件现在位于主存储库中。

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

# Create a 'marker' at the submodule commit being used
cd submodule_path
git switch --create "submodule_merge_marker"
git push --set-upstream origin submodule_merge_marker

# 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/submodule_merge_marker

# 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 --force submodule_path  # will add files instead of commit reference
                                # --force adds files ignored by .gitignore

# Commit and cleanup
git commit -m "remove submodule"
git remote rm submodule_origin

生成的存储库看起来有点奇怪:将有多个初始提交。但它不会给 Git 带来任何问题。

第二种解决方案的一大优点是,您仍然可以对最初位于子模块中的文件运行 git Blame 或 git log 。事实上,这里发生的只是一个存储库中许多文件的重命名,Git 应该自动检测到这一点。如果 git log 仍然存在问题,请尝试一些选项(例如 --follow-M-C) 可以更好地进行重命名和复制检测。

If all you want is to put your submodule code into the main repository, you just need to remove the submodule and re-add the files into the main repo:

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 --force submodule_path  # will add files instead of commit reference
                                # --force adds files ignored by .gitignore
git commit -m "remove submodule"

If you also want to preserve the history of the submodule, you can do a small trick: “merge” the submodule into the main repository, so that the result will be the same as it was before, except that the submodule files are now in the main repository.

In the main module you will need to do the following:

# Create a 'marker' at the submodule commit being used
cd submodule_path
git switch --create "submodule_merge_marker"
git push --set-upstream origin submodule_merge_marker

# 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/submodule_merge_marker

# 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 --force submodule_path  # will add files instead of commit reference
                                # --force adds files ignored by .gitignore

# Commit and cleanup
git commit -m "remove submodule"
git remote rm submodule_origin

The resulting repository will look a bit weird: there will be more than one initial commit. But it won’t cause any problems for Git.

A big advantage of this second solution is that you can still run git blame or git log on the files which were originally in submodules. In fact, what happens here is just a renaming of many files inside one repository, and Git should automatically detect this. If you still have problems with git log, try some options (e.g., --follow, -M, -C) which do better rename and copy detection.

绿阴红影里的.如风往事 2024-08-18 05:53:08

我创建了一个脚本,它将子模块转换为简单的目录,同时保留所有文件历史记录。它不会遇到其他解决方案所遇到的 git log --follow 问题。这也是一个非常简单的单行调用,可以为您完成所有工作。祝你好运。

它建立在 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

I've created a script that will translate a submodule to a simple directory, while retaining all file history. It doesn't suffer from the git log --follow <file> issues that the other solutions suffer from. It's also a very easy one-line invocation that does all of the work for you. G'luck.

It 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.

The latest code will be maintained with bugfixes on github at https://github.com/jeremysears/scripts/blob/master/bin/git-submodule-rewrite, but for the sake of proper stackoverflow answer protocol, I've included the solution in its entirety below.

Usage:

$ 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
天涯沦落人 2024-08-18 05:53:08

git 1.8.5(2013 年 11 月)起(不保留历史记录submodule):

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

这将:

  • 取消注册并卸载(即删除的内容)子模块(deinit,因此mv first),
  • 为您清理 .gitmodules (rm),
  • 并删除 特殊条目代表父存储库索引中的子模块SHA1(rm)。

子模块删除完成后(deinitgit rm),您可以将该文件夹重命名回其原始名称,并将其作为常规文件夹添加到 git 存储库中。

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


如果您需要保留子模块的历史记录,请参阅 jsearsanswer,它使用 git filter-branch

Since git 1.8.5 (Nov 2013) (without keeping the history of the submodule):

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

That will:

  • unregister and unload (ie delete the content of) the submodule (deinit, hence the mv first),
  • clean up the .gitmodules for you (rm),
  • and remove the special entry representing that submodule SHA1 in the index of the parent repo (rm).

Once the removal of the submodule is complete (deinit and git rm), you can rename the folder back to its original name and add it to the git repo as a regular folder.

Note: if the submodule was created by an old Git (< 1.8), you might need to remove the nested .git folder within the submodule itself, as commented by Simon East


If you need to keep the history of the submodule, see jsears's answer, which uses git filter-branch.

在你怀里撒娇 2024-08-18 05:53:08
  1. git rm --cached the_submodule_path
  2. .gitmodules 文件中删除子模块部分,或者如果它是唯一的子模块,则删除该文件。
  3. 做一个提交“删除子模块xyz”
  4. git add the_submodule_path
  5. 另一个提交“添加xyz的代码库”

我还没有找到任何更简单的方法。您可以通过 git commit -a 来将 3-5 步压缩为一个步骤 - 这取决于您的喜好。

  1. git rm --cached the_submodule_path
  2. remove the submodule section from the .gitmodules file, or if it's the only submodule, remove the file.
  3. do a commit "removed submodule xyz"
  4. git add the_submodule_path
  5. another commit "added codebase of xyz"

I didn't find any easier way yet. You can compress 3-5 into one step via git commit -a - matter of taste.

余厌 2024-08-18 05:53:08

这里有很多答案,但所有答案似乎都过于复杂,并且可能不符合您的要求。我相信大多数人都想保留自己的历史。

对于此示例,主存储库将为[email protected]: main/main.git 和子模块存储库将是 [email 受保护]:main/child.git。这假设子模块位于父存储库的根目录中。根据需要调整说明。

首先克隆父存储库并删除旧的子模块。

git clone [email protected]:main/main.git
git submodule deinit child
git rm child
git add --all
git commit -m "remove child submodule"

现在我们将把子存储库添加到主存储库的上游。

git remote add upstream [email protected]: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 您可以查看文件的完整历史记录。

Lots of answers here but all of them seem to be overly complex and likely do not do what you want. I am sure most people want to keep their history.

For this example the main repo will be [email protected]:main/main.git and the submodule repo will be [email protected]:main/child.git. This assumes that the submodule is located in the root directory of the parent repo. Adjust the instructions as needed.

Start by cloning the parent repo and removing the old submodule.

git clone [email protected]:main/main.git
git submodule deinit child
git rm child
git add --all
git commit -m "remove child submodule"

Now we will add the child repos upstream to the main repo.

git remote add upstream [email protected]:main/child.git
git fetch upstream
git checkout -b merge-prep upstream/master

The next step assumes that you want to move the files on the merge-prep branch into the same location as the submodule was above although you can easily change the location by changing the file path.

mkdir child

move all folders and files except the .git folder into the child folder.

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

Now you can simply merge your files back into the master branch.

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

Look around and make sure everything looks good before running git push

The one thing you have to remember now is that git log does not by default follow moved files however by running git log --follow filename you can see the full history of your files.

青柠芒果 2024-08-18 05:53:08

我们碰巧为 2 个项目创建了 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

这里我们完全删除子模块关系,以便我们可以将其他存储库就地创建到项目中。

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

这里我们获取要合并的子模块存储库。

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

这里我们开始对两个存储库进行合并操作,但在提交之前停止。

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

这里,我们将子模块中 master 的内容发送到它在添加目录名前缀之前所在的目录。

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

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

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

It happened to us that we created 2 repositories for 2 projects that were so coupled that didn't make any sense to have them separated, so we merged them.

I'll show how to merge the master branches in each first and then I will explain how you can extend this to every branches you got, hope it helps you.

If you got the submodule working, and you want to convert it to a directory in place you can do:

git clone project_uri project_name

Here we do a clean clone to work. For this process you don't need to initialize or update the submodules, so just skip it.

cd project_name
vim .gitmodules

Edit .gitmodules with your favorite editor (or Vim) to remove the submodule you plan to replace. The lines you need to remove should look something like this:

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

After saving the file,

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

Here we remove the submodule relation completely so we can create bring the other repo to the project in-place.

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

Here we fetch the submodule repository to merge.

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

Here we start a merge operation of the 2 repositories, but stop before commit.

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

Here we send the content of master in the submodule to the directory where it was before prefixing a directory name

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

Here we complete the procedure doing a commit of the changes in the merge.

After finishing this you can push, and start again with any other branch to merge, just checkout the branch in you repository that will receive the changes and change the branch you bringing in the merge and read-tree operations.

纸伞微斜 2024-08-18 05:53:08

我找到的最佳答案在这里:

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

本文很好地解释了该过程。

The best answer to this I have found is here:

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

This article explains the procedure very well.

独享拥抱 2024-08-18 05:53:08

这是 @gyim 答案的稍微改进版本(恕我直言)。他正在主工作副本中进行一系列危险的更改,我认为在单独的克隆上进行操作然后最后将它们合并在一起要容易得多。

在一个单独的目录中(为了使错误更容易清理并重试)检查顶级存储库和子存储库。

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

首先编辑子存储库以将所有文件移动到所需的子目录中

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
# remove --allow-unrelated-histories if using git older than 2.9.0
git merge --allow-unrelated-histories $SUBREPO_HEAD

就完成了!安全且没有任何魔法。

Here's a slightly improved version (IMHO) of @gyim's answer. He is doing a bunch of dangerous changes in the main working copy, where I think it's much easier to operate on separate clones and then merge them together at the end.

In a separate directory (to make mistakes easier to clean up and try again) check out both the top repo and the subrepo.

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

First edit the subrepo to move all files into the desired subdirectory

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"

Make a note of the HEAD

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

Now remove the subrepo from the main repo

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"

And finally, just merge them

git fetch ../sub.tmp
# remove --allow-unrelated-histories if using git older than 2.9.0
git merge --allow-unrelated-histories $SUBREPO_HEAD

And done! Safely and without any magic.

佞臣 2024-08-18 05:53:08

对于何时

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"

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

For when

git rm [-r] --cached submodule_path

returns

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

Context: I did rm -r .git* in my submodule folders before realizing that they needed to be de-submoduled in the main project to which I had just added them. I got the above error when de-submoduling some, but not all of them. Anyway, I fixed them by running, (after, of course, the 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"

Note that this doesn't preserve history.

爱已欠费 2024-08-18 05:53:08

根据 VonC 的回答,我创建了一个简单的 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/**"

Based on VonC's answer, I have created a simple bash script that does this. The add at the end has to use wildcards otherwise it will undo the previous rm for the submodule itself. It's important to add the contents of the submodule directory, and not to name the directory itself in the add command.

In a file called git-integrate-submodule:

#!/usr/bin/env bash
mv "$1" "${1}_"
git submodule deinit "$1"
git rm "$1"
mv "${1}_" "$1"
git add "$1/**"
ㄟ。诗瑗 2024-08-18 05:53:08

在主存储库中

  • git rm --cached [submodules_repo]
  • git commit -m “子模块已删除。”
  • git push origin [master]

在子模块 repo

  • rm -rf .git

再次主 repo

  • git add [submodules_repo]
  • git add 。
  • git commit -m“子模块存储库已添加到主模块中。”
  • git push origin [主]

In main repo

  • git rm --cached [submodules_repo]
  • git commit -m "Submodules removed."
  • git push origin [master]

In submodules repo

  • rm -rf .git

Again main repo

  • git add [submodules_repo]
  • git add .
  • git commit -m "Submodules repo added into main."
  • git push origin [master]
走走停停 2024-08-18 05:53:08

我发现(也?)从子模块获取本地提交数据更方便,因为否则我会丢失它们。 (无法推送它们,因为我无法访问该遥控器)。所以我添加了 submodule/.git 作为remote_origin2,获取它的提交并从该分支合并。
不确定我是否仍然需要子模块远程作为源,因为我对 git 还不够熟悉。

I found it more convenient to (also?) fetch local commit data from the submodule, because otherwise I would loose them. (Could not push them as I have not access to that remote). So I added submodule/.git as remote_origin2, fetched it commits and merged from that branch.
Not sure if I still need the submodule remote as origin, since I am not familiar enough with git yet.

椒妓 2024-08-18 05:53:08

这是我发现的最好的&最简单。

在子模块存储库中,您想要从 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-unlated-histories SubmoduleOriginRemote/mergeMe

繁荣完成

历史记录保留

无忧


请注意,这几乎与某些相同其他答案。但这假设您拥有子模块存储库。此外,这还可以轻松获得子模块未来的上游更改。

Here's what I found best & simplest.

In submodule repo, from HEAD you want to merge into main repo:

  • git checkout -b "mergeMe"
  • mkdir "foo/bar/myLib/" (identical path as where you want the files on main repo)
  • git mv * "foo/bar/myLib/" (move all into path)
  • git commit -m "ready to merge into main"

Back in main repo after removing the submodule and clearing the path "foo/bar/myLib":

  • git merge --allow-unrelated-histories SubmoduleOriginRemote/mergeMe

boom done

histories preserved

no worries


Note this nearly identical to some other answers. But this assumes you own submodule repo. Also this makes it easy to get future upstream changes for submodule.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文