un-submodule gitサブモジュール


378

gitサブモジュールのサブモジュールを解除するには(すべてのコードをコアに戻します)?

私がどのように「すべきか」のように、「最良の手順」のように...


5
注:git1.8.3を使用すると、を試すことができます。以下の私の回答をgit submodule deinitご覧ください
VonC

6
誤解するかもしれませんが、git submodule deinitはコードを削除するようです。
Joe Germuska 2013

2
git 1.8.5(2013年11月)以降、以下の私の回答にgit submodule deinit asubmodule ; git rm asubmodule示すように
VonC

回答:


527

サブモジュールコードをメインリポジトリに配置するだけの場合は、サブモジュールを削除して、ファイルをメインリポジトリに再度追加するだけです。

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に問題を引き起こしません。

この2番目のソリューションでは、元々サブモジュールにあったファイルに対してgit blameまたはgit logを実行できるという大きな利点があります。実際、ここで行ったことは、1つのリポジトリ内の多くのファイルの名前を変更することであり、gitはこれを自動検出するはずです。それでもgit logに問題がある場合は、名前の変更/コピーの検出を改善するいくつかのオプション(--follow、-M、-C)を試してください。


3
私が持っているgitリポジトリで2番目の方法(履歴の保存)を行う必要があると思います。上記のコマンドのどの部分がサブモジュールのファイルをサブディレクトリに置くのか説明してください。あなたがそれをマージすると、gitはトップレベルディレクトリのファイルを(その履歴とともに)取り込みますが、git add submodule_pathを実行すると、暗黙的にすべてのファイルに対してgit mvが実行されますか?
Bowie Owens、

5
基本的にそうです。トリックは、gitが名前変更操作を格納しないことです。代わりに、親のコミットを調べることで操作を検出します。以前のコミットに存在していたが、ファイル名が異なるファイルの内容がある場合、名前の変更(またはコピー)と見なされます。上記の手順でgit mergeは、すべてのファイルの「前のコミット」が(マージの2つの「サイド」の1つに)あることを確認します。
gyim

6
gyimに感謝し、プロジェクトを開始しました。物事を2つのリポジトリに分割し、それらをサブモジュールとリンクすることは理にかなっていると思いました。しかし、今は作りすぎだと思われるので、私の歴史を失うことなくそれらを元に戻したいと思います。
Bowie Owens

4
@theduke私にもこの問題がありました。これらの手順を実行する前に、すべてのファイルをサブモジュールリポジトリから、マージしようとしているリポジトリと同じパスを持つディレクトリ構造に移動することで修正できます。メインリポジトリのサブモジュールがfoo /にある場合は、サブモジュールでを実行しますmkdir foo && git mv !(foo) foo && git commit
Chris Down

35
追加するために必要な--allow-unrelated-histories、私はなっていたとして、偽のマージでマージを強制的にfatal: refusing to merge unrelated historiesもっとここに、:github.com/git/git/blob/master/Documentation/RelNotes/...
vaskort

72

git 1.8.5(2013年11月)以降サブモジュールの履歴を保持しない):

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

それは:

  • サブモジュール(、つまり最初の)の登録を解除してアンロード(つまりのコンテンツを削除)、deinitmv
  • クリーンアップ.gitmodulesrm)、
  • 親リポジトリのインデックスで、サブモジュールSHA1を表す特別なエントリを削除します(rm)。

サブモジュールの削除が完了したら(deinitおよびgit rm)、フォルダーの名前を元の名前に戻し、通常のフォルダーとしてgit repoに追加できます。

注:サブモジュールが古いGitリポジトリ(<1.8)で作成された場合は、ネストされた削除する必要があるかもしれない.gitとして、サブモジュール自体の中にフォルダをコメントサイモン・東


サブモジュールの履歴を保持する必要がある場合は、を使用するjsears回答を参照してくださいgit filter-branch


5
これは実際には1.8.4の作業ツリーから削除します(私のサブモジュールdir全体がクリアされました)。
Chris Down

@ChrisDownつまり、deinit一人でサブモジュールから作業ツリーをクリーンアップしましたか?
VonC 2013年

ええ、それはサブモジュールのディレクトリにあるすべてのコンテンツを削除します。
Chris Down

2
@mschuettいいえ、何も不足していません。最初のサブモジュールには.gitがありません。もしそうなら、それはサブモジュールではなくネストされたリポジトリでした。それが、上記のこの回答があなたのケースに当てはまらない理由を説明しています。2つの違いについては、stackoverflow.com/ a/ 34410102/6309を参照してください。
VonC 2016年

1
@VonC私は現在2.9.0.windows.1を使用していますが、サブモジュールははるかに古いバージョンのgitで数年前に作成された可能性があります。最後の追加+コミットを行う前にそのファイルを削除する限り、手順は機能するようです。
Simon East

67

すべてのファイル履歴を保持しながら、サブモジュールを単純なディレクトリに変換するスクリプトを作成しました。git log --follow <file>他のソリューションが抱えている問題に悩まされることはありません。また、すべての作業を行う非常に簡単な1行の呼び出しでもあります。グラック。

彼のブログ投稿「サブモジュールを親リポジトリに統合する」で説明されているLucasJenßによる優れた成果に基づいて構築されていますが、プロセス全体を自動化し、他のいくつかのコーナーケースをクリーンアップします。

最新のコードは、https://github.com/jeremysears/scripts/blob/master/bin/git-submodule-rewriteの githubのバグ修正で維持されますが、適切な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

Ubuntu 16.04では機能しません。Githubリポジトリにプルリクエストを送信しまし
qznc 2017年

1
良いキャッチ、@ qznc。これはOSXでテストされました。それが両方のプラットフォームで通過したら、喜んでマージします。
jsears

@qznc Ubuntu 16.04サポートがマージされ、回答が更新されました。
jsears

2
これが最良の答えであり、すべての履歴を保持します。非常に素晴らしい!
CharlesB 2018年

1
githubのから最新バージョンを使用してWindows 10のGit Bashの2.20.1.1にエラーなしですべての作業を実行しますcurl https://raw.githubusercontent.com/jeremysears/scripts/master/bin/git-submodule-rewrite > git-submodule-rewrite.sh./git-submodule-rewrite.sh <submodule-name>
アレクセイ

32
  1. git rm --cached the_submodule_path
  2. .gitmodulesファイルからサブモジュールセクションを削除するか、それが唯一のサブモジュールである場合は、ファイルを削除します。
  3. コミット「削除されたサブモジュールxyz」を実行します
  4. git add the_submodule_path
  5. 別のコミット「xyzの追加されたコードベース」

まだ簡単な方法は見つかりませんでした。3-5を1ステップで圧縮できますgit commit -a-好みの問題。


6
.gitmodules代わりにすべきではない.submodulesですか?
imz-Ivan Zakharyaschev 2015

1
すべきで.gitmodulesはない.submodules
Mkey

1
サブモジュールフォルダーで作業する.git前に、サブモジュールのディレクトリを削除する必要がありましたgit add
Carson Evans

16

ここにはたくさんの答えがありますが、それらはすべて非常に複雑で、おそらくあなたが望むことをしないようです。ほとんどの人は自分の歴史を守りたいと思っています。

この例では、メインのリポジトリは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ブランチ上のファイルを、サブモジュールが上と同じ場所に移動することを想定しています。

mkdir child

.gitフォルダーを除くすべてのフォルダーとファイルを子フォルダーに移動します。

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

これで、ファイルをマージしてmasterブランチに戻すことができます。

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

周りを見て、実行する前にすべてがよく見えることを確認してください git push

ここで覚えておかなければならないことの1つは、デフォルトではgit logは移動されたファイルを追跡しないことですが、実行git log --follow filenameするとファイルの完全な履歴を確認できます。


2
私は決勝までgit merge merge-prep進み、エラーを受け取りましたfatal: refusing to merge unrelated histories。回避策はこれです:git merge --allow-unrelated-histories merge-prep
ハンブルハッカー2016

@humblehackerありがとう、他の人もこれに遭遇した場合に備えて、少しコメントを追加しました。
mschuett 2016

1
サブモジュールの履歴を保持するための最良の答え。ありがとう@mschuett
Anton Temchenko 2017年

この例では、アップストリームのファイルをchildディレクトリにフェッチする方法があるので、後で移動する必要はありませんか?サブモジュールとメインリポジトリに同じファイル名があります... 2つのファイルをマージしようとしているため、マージの競合が発生します。
Skitterm

たぶん、私はそれを手放しで知りません。私個人的にはちょうどあなたが、彼らはあなたがそれらを引っ張って前にしたいディレクトリに置くように移動しようとしているレポにファイルを移動するコミットになるだろう。
mschuett

12

2つのプロジェクト用に2つのリポジトリーを作成して、それらを分離しても意味がないほど結合したので、それらをマージしました。

最初に各マスターブランチをマージする方法を示し、次に、これを取得したすべてのブランチに拡張する方法を説明します。

サブモジュールが機能していて、適切なディレクトリに変換したい場合は、次のようにします。

git clone project_uri project_name

ここでは、クリーンなクローンを実行します。このプロセスでは、サブモジュールを初期化または更新する必要がないため、スキップしてください。

cd project_name
vim .gitmodules

.gitmodulesお好みのエディター(またはVim)で編集して、置き換える予定のサブモジュールを削除します。削除する必要がある行は、次のようになります。

[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

ここでは、2つのリポジトリのマージ操作を開始しますが、コミットする前に停止します。

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

ここでは、サブモジュールのマスターのコンテンツを、ディレクトリ名をプレフィックスする前のディレクトリに送信します

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

ここで、マージの変更をコミットする手順を完了します。

これが終了したら、プッシュして、マージする他のブランチから再開できます。変更を受け取るリポジトリ内のブランチをチェックアウトし、マージおよびツリーの読み取り操作で使用するブランチを変更します。


これにはサブモジュールファイルの履歴が保存されていないようです。下に追加されたファイルのgitログに単一のコミットが表示されるだけですdirectory_of_submodule
Anentropic

@Anentropic返信が遅れてすみません。全手順をもう一度行いました(小さな修正を加えました)。この手順では履歴全体が保持されますが、マージポイントがあるため、見つからない場合があります。サブモジュールの履歴を確認するには、「git log」を実行し、マージコミットを探します(例では、「submodule_name is now main part of main project」というメッセージが表示されています)。2つの親コミット(マージ:sdasda asdasd)があり、Gitは2番目のコミットをログに記録し、そこにすべてのサブモジュール/マスター履歴を取得します。
dvicino 2013

私のメモリはぼやけていgit log original_path_of_file_in_submoduleますが、サブモジュールファイルであっても、ファイルのgitリポジトリに登録されているパス(ファイルシステムに存在しない)を実行することで、マージされたサブモジュールファイルの履歴を取得できたと思います現在住んでいますsubmodule_path/new_path_of_file
Anentropic

これは履歴をあまりよく保存せず、パスも間違っています。ツリーフィルターのようなものが必要だと思いますが、深みが足り
ルークH

この答えは廃止され、stackoverflow.com/a/16162228/11343(VonCの答えは)同じですが、より良いん
CharlesB


6

@gyimの回答を少し改善したバージョン(IMHO)を以下に示します。彼はメインの作業コピーで一連の危険な変更を行っています。個別のクローンを操作して、最後にそれらをマージする方がはるかに簡単です。

別のディレクトリで(間違いを簡単にクリーンアップして再試行するため)、トップリポジトリとサブリポジトリの両方をチェックしてください。

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

やった!安全かつ魔法なし。


...どちらの答えですか?ユーザー名を参照する必要がある場合があります。トップの回答は時間の経過とともに変化する可能性があります。
Contango、

@Contangoの回答が更新されました。しかし、トップの回答は依然として400ポイントのリードによるトップの回答です;-)
データレス'10

これは、サブレポにすでにものを含む名前のディレクトリが含まれている場合に機能しますsubrepoか?
19

最後のステップで、次のエラーが発生します。この場合git merge $SUBREPO_HEAD fatal: refusing to merge unrelated historiesに使用する必要がありますgit merge $SUBREPO_HEAD --allow-unrelated-historiesか?それともそれなしで動作するはずであり、私はミスをしましたか?
Ti-m

1
@ Ti-mはい、これは、コミットを共有しない2つの履歴をマージする場合とまったく同じです。私が最初にこれを書いて以来、無関係な履歴に対する警戒心はgitで新しいようです。回答を更新します。
データレス

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"

これは履歴を保存しないことに注意してください。


3

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/**"

0

サブモジュールからローカルコミットデータをフェッチする(また?)と便利です。そうしないと、それらを失う可能性があります。(そのリモートにアクセスできないため、プッシュできませんでした)。それで、submodule / .gitをremote_origin2として追加し、コミットをフェッチして、そのブランチからマージしました。まだgitに慣れていないので、サブモジュールremoteをoriginとしてまだ必要かどうかはわかりません。


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

ブームは終わった

保存された歴史

心配ない


これは他のいくつかの回答とほぼ同じであることに注意してください。しかし、これはあなたが自分のサブモジュールリポジトリを持っていることを前提としています。また、これにより、サブモジュールの将来のアップストリームの変更を簡単に取得できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.