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


119

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

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


2
また、非常に良い答え:stackoverflow.com/questions/359424/...
Rehno Lindeque

回答:


122

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

取り付け

git-subtreegit1.7.11の時点で(contribをインストールする場合)の一部になっているため、すでにインストールされている可能性があります。を実行して確認でき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 git@github.com: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 git@github.com:my-user/new-project.git foo

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


10
git subtree rocks!
Simon Woodside

3
しかし、git-subtreeの目的はサブモジュールの使用を避けることではありませんか?つまり、あなたは確かにgit-subtreeの作者です(ニックネームの衝突がない限り)が、表示されたコマンドはまだ有効であるにもかかわらず、git-subtreeが変更されたように見えます。私はこれを正しくしていますか?
Blaisorblade

17
git-subtreeは1.7.11以降、git(contribをインストールした場合)の一部になりました
Jeremy

8
まあgit rm -rf ./foo削除fooからHEADしかしフィルタリングしないmy-projectの完全な履歴。次に、から始まるサブモジュールgit submodule add git@github.com:my-user/new-project.git fooのみを作成fooHEADます。その点では、スクリプトfilter-branchは「subdirが最初からサブモジュールであるかのように実行」できるため、優れています
Gregory Pakosz

このためのthx-git subtree docsは少し困惑します、そしてこれは(私にとって)私がそれでやりたかった最も明白に有用なことです
hwjp

38

git filter-branchをチェックアウトします。

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

リポジトリを書き直しfoodir/て、プロジェクトルートであるかのように見せ、他のすべての履歴を破棄するには:

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

したがって、たとえば、ライブラリサブディレクトリを独自のリポジトリに変えることができます。
なお、--分離するfilter-branchリビジョンのオプションからオプションを、そして--allすべてのブランチとタグを書き換えます。


1
これは私にはうまくいきました。私が気付いた唯一の欠点は、その結果、すべてのコミットを含む単一のマスターブランチになったことです。
aceofspades 2013年

@aceofspades:なぜそれが悪いのか?
naught101 2013

2
私にとって、gitリポジトリからコミットを抽出する目的は、履歴を保持したいということです。
aceofspades 2013

13

これを行う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力引数の実行を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'

4
git filter-branch(今日?)空のコミットを削除するための組み込みオプション、つまりがあり--prune-emptyます。より良いガイドはgit filter-branchこの質問への回答である:stackoverflow.com/questions/359424/...
Blaisorblade

4

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 git@github.com:myUsername/newRepo.git
git push -u origin master
popd

# replace the folder with a submodule
git rm -rf ./SubFolderName/FolderToBeNewRepo
git submodule add git@github.com: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

3

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

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

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

または、リポジトリを複製し、複製内の用紙を削除し、元のアプリを削除します。これには1分ほどかかりますが、動作することが保証されており、git履歴を精製するよりも重要なことに戻ることができます。また、履歴の冗長コピーによって占有されるハードドライブ領域について心配する必要はありません。

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