19

2 つの SVN リポジトリに存在するプロジェクトがあります。2 番目の SVN リポジトリは、SCM 情報を削除せずに、古い SVN リポジトリのチェックアウトからリポジトリを追加するだけで作成されました。ファイルの内容はバイト単位で同一ですが、関連する SCM メタデータはありません。

新しい SVN リポジトリを取得し、git-svn を介して Git リポジトリに移植しました。ここで、古いリポジトリをインポートし、新しいリポジトリにリンクして、両方の履歴を確認できるようにしたいと考えています。2 つのリポジトリを手でつなぎ合わせずにこれを行う簡単な方法はありますか?

4

2 に答える 2

30

参照: github.comでフォークしたプロジェクトの上に、ローカルGitリポジトリのコミットを再生するには質問(そしてそこでの私の答え)、状況は少し異なりますが、私は思います。


少なくとも3つの可能性があります。

  • 移植片を使用して2つの履歴を結合しますが、履歴を書き換えないでください。これは、あなた(および同じ移植片を持っている人)は完全な履歴を持ち、他のユーザーはより小さなリポジトリを持っていることを意味します。これにより、誰かが変換されたリポジトリの上でより短い履歴で作業を開始した場合に、履歴の書き換えに関する問題も回避されます。

  • グラフトを使用して2つの履歴を結合し、「gitlog」または「gitk」(または他のGit履歴ブラウザー/ビューアー)を使用して正しいことを確認してから、gitfilter -branchを使用して履歴を書き換えます。次に、移植ファイルを削除できます。これは、書き換えられたリポジトリからクローンを作成(フェッチ)するすべての人が、完全な参加履歴を取得することを意味します。しかし、誰かがすでに変換された短い履歴リポジトリに基づいて作業している場合、履歴の書き換えは大したことではありません(ただし、この場合は当てはまらない可能性があります)。

  • git replaceを使用して、2つの履歴を結合します。refs/replace/これにより、ユーザーは、フェッチする(完全な履歴を取得する)かどうか(短い履歴を取得する)を選択することで、完全な履歴を取得するか、現在の履歴のみを取得するかを選択できます。残念ながら、これには現在、まだリリースされていないバージョンのGitを使用するか、開発(「マスター」)バージョンを使用するか、1.6.5のリリース候補の1つを使用する必要があります。refs/replace/階層は、次のGitバージョン1.6.5で計画されています。


以下に、これらすべてのメソッドのステップバイステップの説明があります:グラフト(ローカル)、グラフトを使用した履歴の書き換え、およびrefs /replace/。

すべての場合において、単一のリポジトリに現在と過去の両方のリポジトリ履歴があると想定しています(git remote addを使用して別のリポジトリから履歴を追加できます)。また、短い履歴リポジトリのブランチ(の1つ)は「master」という名前であり、現在の履歴を添付する履歴リポジトリのブランチ(commit)は「history」と呼ばれていると仮定します。独自のブランチ名(またはコミットID)に置き換える必要があります。

アタッチするコミットを見つける(短い履歴のルート)

まず、完全な履歴に添付する短い履歴のコミット(のSHA-1識別子)を見つける必要があります。これは、短い履歴の最初のコミット、つまりルートコミット(親なしのコミット)になります。

それを見つけるには2つの方法があります。他にルートコミットがないことが確実な場合は、次を使用して、トポロジカル順序で最後の(一番下の)コミットを見つけることができます。

$ git rev-list --topo-order master | tail -n 1

(ここtail -n 1で、出力の最後の行を取得するために使用されます。それがない場合は、使用する必要はありません。)

複数のルートコミットの可能性がある場合は、次のワンライナーを使用してすべての親のないコミットを見つけることができます。

$ git rev-list --parents master | grep -v ' '

(ここでgrep -v ' '、つまり、一重引用符の間のスペースは、親を持つすべてのコミットを除外するために使用されます)。次に、git show <commit>それらのコミットが複数あるかどうかを(たとえば ""を使用して)チェックし、以前の履歴に添付するコミットを選択する必要があります。

このコミットをTAILと呼びましょう。次を使用してシェル変数に保存できます(より単純な方法が機能すると仮定します)。

$ TAIL=$(git rev-list --topo-order master | tail -n 1)

以下の説明$TAILでは、現在の(短い)履歴の一番下のコミットのSHA-1を置き換えるか、シェルに置き換えを許可する必要があることを意味します。

アタッチするコミットを見つける(履歴リポジトリの上部)

この部分は単純です。コミットのシンボリック名をSHA-1識別子に変換する必要があります。これは「gitrev-parse」を使用して行うことができます。

$ git rev-parse --verify history^0

(ここで、「history」がタグである場合に備えて、「history」の代わりに「history ^ 0」が使用されます。タグオブジェクトではなく、コミットのSHA-1が必要です)。同様に、アタッチするコミットを見つけるのと同じように、このコミットIDにTOPという名前を付けましょう。次を使用して、シェル変数に保存できます。

$ TOP=$(git rev-parse --verify history^0)

グラフトファイルを使用した履歴の結合

にあるgraftsファイル.git/info/grafts(このメカニズムを使用する場合は、このファイルが存在しない場合は作成する必要があります)は、コミットの親情報を置き換えるために使用されます。これは行ベースの形式であり、各行には、変更するコミットのSHA-1が含まれ、その後に、特定のコミットを親として持つ必要のある、スペースで区切られたコミットのリストが0個以上含まれます。git rev-list --parents <revision>" "が出力するのと同じフォーマット。

親を持たない$TAILcommitで、単一の親として$TOPを使用する必要があります。したがって、info/graftsファイルには、$ TAILコミットのSHA-1を含む行があり、$TOPコミットのSHA-1によってスペースで区切られている必要があります。これには、次のワンライナーを使用できます(git filter-branchドキュメントの例も参照してください)。

$ echo "$TAIL $TOP" >> .git/info/grafts

ここで、「git log」、「git log --graph」、「gitk」、またはその他の履歴ブラウザを使用して、履歴に正しく参加したことを確認する必要があります。

グラフトファイルによる履歴の書き換え

これにより履歴が変わることに注意してください!

グラフトファイルに記録されている履歴を永続的にするには、「gitfilter-branch」を使用して必要なブランチを書き換えるだけで十分です。書き直す必要のあるブランチが1つしかない場合(「マスター」)、次のように簡単にできます。

$ git filter-branch $TOP..master

(これは最小限のコミットセットのみを処理します)。参加履歴の影響を受けるブランチが他にもある場合は、次を使用できます。

$ git filter-branch --all

これで、graftsファイルを削除できます。すべてが希望どおりかどうかを確認し、バックアップを削除しますrefs/original/(詳細については、「git filter-branch」のドキュメントを参照してください)。

refs /replace/メカニズムの使用

これは、graftsファイルの代替手段です。転送可能であるという利点があるため、短い履歴を公開して書き直すことができない場合(他の人が短い履歴に基づいて作業しているため)、refs /replace/を使用するのが良い解決策になる可能性があります...少なくともGitバージョン1.6.5がリリースされたとき。

refs / replace /メカニズムは、graftsファイルとは動作が異なります。親の情報を変更する代わりに、オブジェクトを置き換えます。したがって、最初に、$ TAILと同じプロパティを持ち、親として$TOPを持つcommitオブジェクトを作成する必要があります。

使用できます

$ git cat-file commit $TAIL > TAIL_COMMIT

(一時ファイルの名前は単なる例です)。

次に、「TAIL_COMMIT」ファイルを編集する必要があります(次のようになります)。

ツリー2b5bfdf7798569e0b59b16eb9602d5fa572d6038
著者JoeRHacker 1112911993 -0700
コミッターJoeRHacker 1112911993 -0700

新しいリポジトリに移動した後の「プロジェクト」の初期リビジョン

次に、「tree」ヘッダーと「author」ヘッダーの間に「parent $ TOP」($TOPをSHA-1idに展開する必要があります!)の行を入れて、$TOPを親として追加する必要があります。'TAIL_COMMIT'を編集すると、次のようになります。

ツリー2b5bfdf7798569e0b59b16eb9602d5fa572d6038
親0f6592e3c2f2fe01f7b717618e570ad8dff0bbb1
著者JoeRHacker 1112911993 -0700
コミッターJoeRHacker 1112911993 -0700

新しいリポジトリに移動した後の「プロジェクト」の初期リビジョン

必要に応じて、コミットメッセージを編集できます。

次に、 git hash-objectを使用して、リポジトリに新しいコミットを作成する必要があります。このコマンドの結果を保存する必要があります。これは、たとえば次のように、新しいコミットオブジェクトのSHA-1です。

$ NEW_TAIL=$(git hash-object -t commit -w TAIL_COMMIT)

(ここで、' -w'オプションは、オブジェクトを実際にリポジトリに書き込むためのものです)。

最後に、gitreplaceを使用して$TAILを$NEW_TAILに置き換えます

$ git replace $TAIL $NEW_TAIL

次に、履歴が正しいかどうかを(「gitlog」または他の履歴ビューアを使用して)確認する必要があります。

これで、完全な履歴が必要な場合は+refs/replace/*:refs/replace/*、プルrefspecの1つとして''を追加する必要があります。

最後の注意: 私はこの解決策をチェックしていませんので、あなたのマイレージは変わるかもしれません

于 2009-10-10T09:33:36.823 に答える
5

まず、2 つの履歴を接続するグラフト ポイントを作成します。次に、リポジトリに対してgit filter-branchを実行して、変更を永続的にします。これにより、グラフトの下流にあるすべてのコミットのコミット ID が変更されます。

于 2009-08-03T03:28:24.257 に答える