un-submodule一个git子模块

Qui*_*fox 359 git git-submodules

如何取消子模块git子模块(将所有代码重新放入核心)?

就像"应该"我一样,如"最佳程序"......

gyi*_*yim 502

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

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"
Run Code Online (Sandbox Code Playgroud)

如果你还想保留子模块的历史,你可以做一个小技巧:将子模块"合并"到主存储库中,这样结果就像以前一样,除了子模块文件现在在主存储库.

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

# 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
Run Code Online (Sandbox Code Playgroud)

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

在第二个解决方案中,您将拥有一个很大的优势,即您仍然可以对最初位于子模块中的文件运行git blame或git log.实际上你在这里做的是重命名一个存储库中的许多文件,git应该自动检测这个.如果您仍然遇到git log问题,请尝试一些选项( - follow,-M,-C),这些选项可以更好地重命名/复制检测.

  • 需要添加`--allow-unrelated-histories`来强制合并虚假合并,因为我变得"致命:拒绝合并不相关的历史",更多信息来自:https://github.com/git/git/blob /master/Documentation/RelNotes/2.9.0.txt#L58-L68 (29认同)
  • 感谢gyim,我开始了一个项目,我认为将事物分成几个存储库并将它们与子模块链接在一起是有意义的.但是现在它似乎过度设计,我希望将它们组合在一起而不会丢失我的历史. (6认同)
  • 基本上,是的.诀窍是git不存储重命名操作:相反,它通过查看父提交来检测它们.如果前一次提交中存在文件内容,但文件名不同,则将其视为重命名(或复制).在上面的步骤中,`git merge`确保每个文件都有一个"先前提交"(在合并的两个"边"之一). (5认同)
  • @theduke我也有这个问题.在执行这些步骤之前,可以通过将子模块存储库中的所有文件移动到与您要合并到的存储库具有*相同路径*的目录结构来修复它:ie.如果主存储库中的子模块在foo /中,则在子模块中执行`mkdir foo && git mv!(foo)foo && git commit`. (4认同)
  • 我想我需要在我的一些git repos上做你的第二种方法(历史保留).您能否解释一下上述命令的哪一部分导致子模块中的文件最终出现在子目录中?是你在进行合并时,git引入顶级目录中的文件(及其历史记录),但是当你执行git add submodule_path时,它是不是每个文件都有一个git mv? (3认同)
  • 它不保留历史记录,如果我在“git log subdirectory_where_submodule”之后输入,它不会显示所有历史记录。 (3认同)
  • 是的,由于文件位于历史的"子模块端"的根目录中,git将在提交中看到合并和一堆重命名. (2认同)

Von*_*onC 69

git 1.8.5(2013年11月)(不保留子模块的历史):

mv yoursubmodule yoursubmodule_tmp
git submodule deinit yourSubmodule
git rm yourSubmodule
mv yoursubmodule_tmp yoursubmodule
git add yoursubmodule
Run Code Online (Sandbox Code Playgroud)

那会:

  • 取消注册和卸载(即删除内容)子模块(deinit因此是mv 第一个),
  • .gitmodules为你清理(rm),
  • 并删除父repo()索引中表示子模块SHA1 的特殊条目rm.

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

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


如果你需要保持子模块的历史,看到jsears答案,它使用git filter-branch.

  • 这实际上确实从1.8.4中的工作树中删除了它(我的整个子模块目录已被清除). (5认同)
  • @mschuett不,你没有遗漏任何东西:一个子模块首先没有.git.如果你是这种情况,它是一个嵌套的回购,而不是一个子模块.这就解释了为什么上述答案不适用于您的情况.有关两者之间的差异,请参阅http://stackoverflow.com/a/34410102/6309. (2认同)

jse*_*ars 58

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

它建立在LucasJenß的出色工作之上,在他的博客文章" 将一个子模块集成到父存储库中 "中进行了描述,但是它自动化了整个过程并清理了其他一些极端情况.

最新的代码将通过https://github.com/jeremysears/scripts/blob/master/bin/git-submodule-rewrite在github上维护错误修复,但为了正确的stackoverflow应答协议,我已经包含了整个解决方案如下.

用法:

$ git-submodule-rewrite <submodule-name>
Run Code Online (Sandbox Code Playgroud)

混帐子模块重写:

#!/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
Run Code Online (Sandbox Code Playgroud)

  • 这是最好的答案,保留了整个历史。非常好! (2认同)

Mar*_*rth 23

  1. git rm --cached the_submodule_path
  2. .gitmodules文件中删除子模块部分,或者如果它是唯一的子模块,则删除该文件.
  3. 做一个提交"删除子模块xyz"
  4. git add the_submodule_path
  5. 另一个提交"添加了xyz的代码库"

我还没有找到任何更简单的方法.你可以通过git commit -a味道来压缩3-5到一步.

  • 不应该是`.gitmodules`而不是`.submodules`? (6认同)
  • 我必须先删除子模块的 `.git` 目录,然后 `git add` 才能在子模块文件夹上工作 (2认同)

msc*_*ett 16

这里有很多答案,但所有这些答案似乎过于复杂,可能不会做你想要的.我相信大多数人都想保留他们的历史.

对于这个例子,主要的回购将是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"
Run Code Online (Sandbox Code Playgroud)

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

git remote add upstream git@site.com:main/child.git
git fetch upstream
git checkout -b merge-prep upstream/master
Run Code Online (Sandbox Code Playgroud)

下一步假设您希望将merge-prep分支上的文件移动到与子模块相同的位置,尽管您可以通过更改文件路径轻松更改位置.

mkdir child
Run Code Online (Sandbox Code Playgroud)

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

git add --all
git commit -m "merge prep"
Run Code Online (Sandbox Code Playgroud)

现在,您只需将文件合并回主分支即可.

git checkout master
git merge merge-prep # --allow-unrelated-histories merge-prep flag may be required 
Run Code Online (Sandbox Code Playgroud)

环顾四周,确保在跑步前一切都很好看 git push

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

  • 我一直到最后的`git merge merge-prep`并收到错误`致命:拒绝合并不相关的历史记录`.解决方法是:`git merge --allow-unrelated-histories merge-prep`. (2认同)

dvi*_*ino 12

我们碰巧在2个项目中创建了2个存储库,这些存储库是如此耦合,将它们分开是没有任何意义的,所以我们合并它们.

我将展示如何在每个分支中合并主分支,然后我将解释如何将其扩展到您获得的每个分支,希望它可以帮助您.

如果您的子模块正常工作,并且您想将其转换为适当的目录,您可以执行以下操作:

git clone project_uri project_name
Run Code Online (Sandbox Code Playgroud)

在这里,我们做一个干净的克隆工作.对于此过程,您无需初始化或更新子模块,因此请跳过它.

cd project_name
vim .gitmodules
Run Code Online (Sandbox Code Playgroud)

.gitmodules使用您喜欢的编辑器(或Vim)进行编辑,以删除您要替换的子模块.您需要删除的行应如下所示:

[submodule "lib/asi-http-request"]
    path = lib/asi-http-request
    url = https://github.com/pokeb/asi-http-request.git
Run Code Online (Sandbox Code Playgroud)

保存文件后,

git rm --cached directory_of_submodule
git commit -am "Removed submodule_name as submodule"
rm -rf directory_of_submodule
Run Code Online (Sandbox Code Playgroud)

这里我们完全删除子模块关系,这样我们就可以创建其他repo到项目的就地.

git remote add -f submodule_origin submodule_uri
git fetch submodel_origin/master
Run Code Online (Sandbox Code Playgroud)

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

git merge -s ours --no-commit submodule_origin/master
Run Code Online (Sandbox Code Playgroud)

这里我们开始2个存储库的合并操作,但在提交之前停止.

git read-tree --prefix=directory_of_submodule/ -u submodule_origin/master
Run Code Online (Sandbox Code Playgroud)

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

git commit -am "submodule_name is now part of main project"
Run Code Online (Sandbox Code Playgroud)

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

完成此操作后,您可以推送,并再次与任何其他分支合并,只需检查您将收到更改的存储库中的分支,并更改您在合并和读取树操作中引入的分支.


dat*_*ess 6

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

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

git clone ../main_repo main.tmp
git clone ../main_repo/sub_repo sub.tmp
Run Code Online (Sandbox Code Playgroud)

首先编辑 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"
Run Code Online (Sandbox Code Playgroud)

记下 HEAD

SUBREPO_HEAD=`git reflog | awk '{ print $1; exit; }'`
Run Code Online (Sandbox Code Playgroud)

现在从主仓库中删除子仓库

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"
Run Code Online (Sandbox Code Playgroud)

最后,只需合并它们

git fetch ../sub.tmp
# remove --allow-unrelated-histories if using git older than 2.9.0
git merge --allow-unrelated-histories $SUBREPO_HEAD
Run Code Online (Sandbox Code Playgroud)

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


Luk*_*e H 6

我发现的最佳答案是:

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

本文非常好地解释了该过程.