web-dev-qa-db-ja.com

gitサブディレクトリを抽出し、そこからサブモジュールを作成する方法は?

数か月前にプロジェクトを開始し、すべてをメインディレクトリに保存しました。私のメインディレクトリ「Project」には、さまざまなものを含むいくつかのサブディレクトリがあります。Project/ paperにはLaTeX Project/sourcecode/RailsAppで記述されたドキュメントが含まれ、my Rails app。

「Project」はGIT化されており、「paper」ディレクトリと「RailsApp」ディレクトリの両方で多くのコミットが行われています。さて、「RailsApp」にcruisecontrol.rbを使用したいので、履歴を失うことなく「RailsApp」からサブモジュールを作成する方法があるかどうか疑問に思います。

112
patch

最近では、git filter-branchを手動で使用するよりもはるかに簡単な方法があります。 git subtree

Installation

[〜#〜] note [〜#〜]git-subtreegitの一部になりました(contribをインストールする場合) 1.7.11の時点で、既にインストールされている可能性があります。 git subtreeを実行して確認できます。


ソースからgit-subtreeをインストールするには(古いバージョンのgitの場合):

git clone https://github.com/apenwarr/git-subtree.git

cd git-subtree
Sudo rsync -a ./git-subtree.sh /usr/local/bin/git-subtree

または、manページとすべてが必要な場合

make doc
make install

使用法

大きいチャンクを小さいチャンクに分割します。

# Go into the project root
cd ~/my-project

# Create a branch which only contains commits for the children of 'foo'
git subtree split --prefix=foo --branch=foo-only

# Remove 'foo' from the project
git rm -rf ./foo

# Create a git repo for 'foo' (assuming we already created it on github)
mkdir foo
pushd foo
git init
git remote add Origin [email protected]:my-user/new-project.git
git pull ../ foo-only
git Push Origin -u master
popd

# Add 'foo' as a git submodule to `my-project`
git submodule add [email protected]:my-user/new-project.git foo

詳細なドキュメント(manページ)については、git-subtree.txtをお読みください。

116
apenwarr

チェックアウト git filter-branch

マニュアルページの Examplesセクション は、すべての履歴を保持し、他のファイル/ディレクトリの履歴を破棄しながら、サブディレクトリを独自のプロジェクトに抽出する方法を示しています再探しています)。

リポジトリをリライトして、foodir/はプロジェクトのルートであり、他のすべての履歴を破棄します。

   git filter-branch --subdirectory-filter foodir -- --all

したがって、たとえば、ライブラリのサブディレクトリを独自のリポジトリに変えることができます。
注意してください--filter-branchリビジョンオプションのオプション、および--allすべてのブランチとタグを書き換えます。

38
Pat Notz

これを行う1つの方法は逆です-保持するファイル以外のすべてを削除します。

基本的に、リポジトリのコピーを作成してから、 git filter-branch を使用して、ファイル/フォルダ以外をすべて削除します保ちたい.

たとえば、ファイルtvnamer.pyを新しいリポジトリに抽出するプロジェクトがあります。

git filter-branch --tree-filter 'for f in *; do if [ $f != "tvnamer.py" ]; then rm -rf $f; fi; done' HEAD

git filter-branch --tree-filterを使用して各コミットを実行し、コマンドを実行して、結果のディレクトリコンテンツを再コミットします。これは非常に破壊的であるため(リポジトリのコピーでのみこれを行う必要があります!)、しばらく時間がかかります(300のコミットと約20のファイルがあるリポジトリで約1分)

上記のコマンドは、各リビジョンで次のシェルスクリプトを実行するだけです。もちろん、変更する必要があります(tvnamer.pyの代わりにサブディレクトリを除外するには):

for f in *; do
    if [ $f != "tvnamer.py" ]; then
        rm -rf $f;
    fi;
done

最大の明白な問題は、残りのファイルと関係がない場合でも、すべてのコミットメッセージが残ることです。スクリプト git-remove-empty-commits 、これを修正します。

git filter-branch --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; fi'

-f force引数run filter-branchを使用して、refs/original/(基本的にはバックアップ)内のすべてのものを再度使用する必要があります。

もちろん、これは完全ではありません。たとえば、コミットメッセージが他のファイルについて言及している場合ですが、それはgit currentが許す限り近い(とにかく私が知る限り)。

繰り返しますが、これはリポジトリのコピーでのみ実行してください!-要約すると、「thisismyfilename.txt」以外のすべてのファイルを削除するには:

git filter-branch --tree-filter 'for f in *; do if [ $f != "thisismyfilename.txt" ]; then rm -rf $f; fi; done' HEAD
git filter-branch -f --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; fi'
13
dbr

CoolAJ86apenwarr の両方の答えは非常に似ています。どちらか一方から欠落しているビットを理解しようとして、2つの間を行き来しました。以下はそれらの組み合わせです。

まず、Git Bashをgitリポジトリのルートに移動して分割します。ここの私の例では、それは~/Documents/OriginalRepo (master)です

_# move the folder at prefix to a new branch
git subtree split --prefix=SubFolderName/FolderToBeNewRepo --branch=to-be-new-repo

# create a new repository out of the newly made branch
mkdir ~/Documents/NewRepo
pushd ~/Documents/NewRepo
git init
git pull ~/Documents/OriginalRepo to-be-new-repo

# upload the new repository to a place that should be referenced for submodules
git remote add Origin [email protected]:myUsername/newRepo.git
git Push -u Origin master
popd

# replace the folder with a submodule
git rm -rf ./SubFolderName/FolderToBeNewRepo
git submodule add [email protected]:myUsername/newRepo.git SubFolderName/FolderToBeNewRepo
git branch --delete --force to-be-new-repo
_

以下は、カスタマイズ可能な名前を置き換え、代わりにhttpsを使用した上記のコピーです。ルートフォルダーは~/Documents/_Shawn/UnityProjects/SoProject (master)になりました

_# move the folder at prefix to a new branch
git subtree split --prefix=Assets/SoArchitecture --branch=so-package

# create a new repository out of the newly made branch
mkdir ~/Documents/_Shawn/UnityProjects/SoArchitecture
pushd ~/Documents/_Shawn/UnityProjects/SoArchitecture
git init
git pull ~/Documents/_Shawn/UnityProjects/SoProject so-package

# upload the new repository to a place that should be referenced for submodules
git remote add Origin https://github.com/Feddas/SoArchitecture.git
git Push -u Origin master
popd

# replace the folder with a submodule
git rm -rf ./Assets/SoArchitecture
git submodule add https://github.com/Feddas/SoArchitecture.git
git branch --delete --force so-package
_
4
ShawnFeatherly

ファイルの一部のサブセットを新しいリポジトリに転送し、履歴を保持したい場合は、基本的に完全に新しい履歴になります。これが機能する方法は、基本的に次のとおりです。

  1. 新しいリポジトリを作成します。
  2. 古いリポジトリのリビジョンごとに、モジュールへの変更を新しいリポジトリにマージします。これにより、既存のプロジェクト履歴の「コピー」が作成されます。

小さくても毛むくじゃらのスクリプトを書くことを気にしないなら、これを自動化するのはいくぶん簡単なはずです。わかりやすい、はい、でも苦痛です。過去にGitで歴史の書き換えを行った人は、それを検索できます。

または:リポジトリを複製し、複製内の論文を削除し、オリジナルのアプリを削除します。これには1分かかりますが、動作することが保証されており、git履歴を浄化しようとするよりも重要なことに戻ることができます。また、履歴の冗長コピーが占めるハードドライブの容量について心配する必要はありません。

3
Dietrich Epp