123

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

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

4

5 に答える 5

128

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

インストール

NOTE 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

または、マニュアルページとすべてが必要な場合

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

于 2009-08-20T18:22:10.333 に答える
41

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

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

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

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

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

于 2009-05-30T17:28:55.003 に答える
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'

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

もちろん、これは決して完璧ではありません。たとえば、コミットメッセージに他のファイルが記載されている場合ですが、(とにかく私が知っている限りでは)gitcurrentが許す限り近いです。

繰り返しますが、これはリポジトリのコピーでのみ実行してください。-しかし、要約すると、「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'
于 2009-05-30T18:29:23.333 に答える
3

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

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

小さくて複雑なスクリプトを書くことを気にしないのであれば、これを自動化するのはいくぶん簡単です。率直ですが、痛みもあります。人々は過去に Git で履歴の書き換えを行ったことがあります。それを検索できます。

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

于 2009-05-28T10:29:55.603 に答える