章 ▾ 第2版

9.2 Gitとその他のシステム - Gitへの移行

Gitへの移行

もし既存のコードベースが他のVCSにあるが、Gitを使い始めることを決めたのであれば、何らかの方法でプロジェクトを移行する必要があります。このセクションでは、一般的なシステム用のいくつかのインポーターについて説明し、独自のカスタムインポーターを開発する方法を示します。多くのユーザーが乗り換えている主要なプロフェッショナル向けSCMシステムからデータをインポートする方法を学びます。これらのシステムには高品質なツールが簡単に入手できるためです。

Subversion

もし`git svn`の使用に関する前のセクションを読んだ場合、それらの手順を使って簡単にリポジトリを`git svn clone`できます。その後、Subversionサーバーの使用を停止し、新しいGitサーバーにプッシュして、そちらを使い始めることができます。履歴が必要な場合、Subversionサーバーからデータを取得できる速さで(これは時間がかかるかもしれません)、それを行うことができます。

しかし、インポートは完璧ではありません。そして、非常に時間がかかるため、正しく行うに越したことはありません。最初の問題は著者情報です。Subversionでは、コミットする各人物がシステム上にユーザーを持ち、それがコミット情報に記録されます。前のセクションの例では、`blame`の出力や`git svn log`など、いくつかの場所に`schacon`と表示されています。これをより良いGitの著者データにマッピングしたい場合、SubversionユーザーからGit著作者へのマッピングが必要です。次のような形式でマッピングを記述した`users.txt`というファイルを作成してください

schacon = Scott Chacon <schacon@geemail.com>
selse = Someo Nelse <selse@geemail.com>

SVNが使用する著者名のリストを取得するには、これを実行します

$ svn log --xml --quiet | grep author | sort -u | \
  perl -pe 's/.*>(.*?)<.*/$1 = /'

これはログ出力をXML形式で生成し、著者情報を含む行のみを保持し、重複を破棄し、XMLタグを除去します。明らかに、これは`grep`、`sort`、`perl`がインストールされているマシンでのみ機能します。その後、その出力を`users.txt`ファイルにリダイレクトして、各エントリの横に対応するGitユーザーデータを追加できます。

Windowsマシンでこれを試している場合、ここで問題が発生する可能性があります。Microsoftは、https://learn.microsoft.com/en-us/azure/devops/repos/git/perform-migration-from-svn-to-gitでいくつかの良いアドバイスとサンプルを提供しています。

このファイルを`git svn`に提供することで、著者データをより正確にマッピングするのに役立てることができます。また、`clone`または`init`コマンドに`--no-metadata`を渡すことで、`git svn`にSubversionが通常インポートするメタデータを含めないように指示することもできます。このメタデータには、Gitがインポート中に生成する各コミットメッセージ内の`git-svn-id`が含まれます。これはGitログを肥大化させ、少し不明瞭にする可能性があります。

Gitリポジトリで行われたコミットを元のSVNリポジトリにミラーリングしたい場合は、メタデータを保持する必要があります。コミットログに同期が不要な場合は、`--no-metadata`パラメータを省略しても構いません。

これにより、`import`コマンドは次のようになります

$ git svn clone http://my-project.googlecode.com/svn/ \
      --authors-file=users.txt --no-metadata --prefix "" -s my_project
$ cd my_project

これで、`my_project`ディレクトリにより良いSubversionインポートができました。次のようなコミットではなく

commit 37efa680e8473b615de980fa935944215428a35a
Author: schacon <schacon@4c93b258-373f-11de-be05-5f7a86268029>
Date:   Sun May 3 00:12:22 2009 +0000

    fixed install - go to trunk

    git-svn-id: https://my-project.googlecode.com/svn/trunk@94 4c93b258-373f-11de-
    be05-5f7a86268029

次のようになります

commit 03a8785f44c8ea5cdb0e8834b7c8e6c469be2ff2
Author: Scott Chacon <schacon@geemail.com>
Date:   Sun May 3 00:12:22 2009 +0000

    fixed install - go to trunk

著者フィールドがはるかに見栄えが良くなるだけでなく、`git-svn-id`もなくなりました。

インポート後のクリーンアップも少し行うべきです。まず、`git svn`が設定した奇妙な参照をクリーンアップする必要があります。最初にタグを奇妙なリモートブランチではなく実際のタグになるように移動し、次に残りのブランチをローカルになるように移動します。

タグを適切なGitタグとして移動するには、以下を実行します

$ for t in $(git for-each-ref --format='%(refname:short)' refs/remotes/tags); do git tag ${t/tags\//} $t && git branch -D -r $t; done

これにより、`refs/remotes/tags/`で始まるリモートブランチだった参照が、実際の(軽量な)タグになります。

次に、`refs/remotes`以下の残りの参照をローカルブランチに移動します

$ for b in $(git for-each-ref --format='%(refname:short)' refs/remotes); do git branch $b refs/remotes/$b && git branch -D -r $b; done

Subversionでは1つのブランチしか表示されないのに、`@xxx`(xxxは数字)という接尾辞が付いた余分なブランチが表示されることがあります。これは実際には「peg-revisions(ピンリビジョン)」と呼ばれるSubversionの機能で、Gitには構文上の対応するものがありません。そのため、`git svn`は、そのブランチのピンリビジョンを指定するためにSubversionで記述するのと同じ方法で、SVNのバージョン番号をブランチ名に単純に追加します。ピンリビジョンにもう関心がない場合は、単純にそれらを削除してください

$ for p in $(git for-each-ref --format='%(refname:short)' | grep @); do git branch -D $p; done

これで、古いブランチはすべて実際のGitブランチになり、古いタグはすべて実際のGitタグになりました。

最後にクリーンアップすべきことが一つあります。残念ながら、`git svn`はSubversionのデフォルトブランチにマッピングされる`trunk`という余分なブランチを作成しますが、`trunk`参照は`master`と同じ場所を指しています。`master`の方がGitの慣用的であるため、余分なブランチを削除する方法を次に示します

$ git branch -d trunk

最後に行うことは、新しいGitサーバーをリモートとして追加し、そこにプッシュすることです。サーバーをリモートとして追加する例を次に示します

$ git remote add origin git@my-git-server:myrepository.git

すべてのブランチとタグをプッシュしたいので、これを実行できます

$ git push origin --all
$ git push origin --tags

すべてのブランチとタグは、新しくきれいなインポートとして新しいGitサーバー上にあるはずです。

Mercurial

MercurialとGitはバージョン表現に関してかなり似たモデルを持っており、Gitの方が多少柔軟であるため、MercurialリポジトリからGitへの変換は、「hg-fast-export」というツールを使用すればかなり簡単です。このツールのコピーが必要になります

$ git clone https://github.com/frej/fast-export.git

変換の最初のステップは、変換したいMercurialリポジトリの完全なクローンを取得することです

$ hg clone <remote repo URL> /tmp/hg-repo

次のステップは、著者マッピングファイルを作成することです。Mercurialはチェンジセットの著者フィールドに入れるものに関してGitよりも多少寛容なので、ここでクリーンアップするのが良いでしょう。これを生成するのは、`bash`シェルで1行のコマンドです

$ cd /tmp/hg-repo
$ hg log | grep user: | sort | uniq | sed 's/user: *//' > ../authors

これはプロジェクトの履歴の長さにもよりますが、数秒かかります。その後、`/tmp/authors`ファイルは次のようになります

bob
bob@localhost
bob <bob@company.com>
bob jones <bob <AT> company <DOT> com>
Bob Jones <bob@company.com>
Joe Smith <joe@company.com>

この例では、同じ人物(Bob)が4つの異なる名前でチェンジセットを作成しており、そのうちの1つは実際に正しく見え、別の1つはGitコミットでは完全に無効になります。Hg-fast-exportは、各行をルール「`""=""`」に変えることでこれを修正できます。これは``を``にマッピングします。および文字列内では、Pythonの`string_escape`エンコーディングが解釈するすべてのエスケープシーケンスがサポートされます。著者マッピングファイルに一致する``が含まれていない場合、その著者は変更されずにGitに送られます。すべてのユーザー名が問題ないように見える場合、このファイルはまったく必要ありません。この例では、ファイルは次のようになるはずです

"bob"="Bob Jones <bob@company.com>"
"bob@localhost"="Bob Jones <bob@company.com>"
"bob <bob@company.com>"="Bob Jones <bob@company.com>"
"bob jones <bob <AT> company <DOT> com>"="Bob Jones <bob@company.com>"

同じ種類のマッピングファイルは、Mercurialの名前がGitで許可されていない場合に、ブランチやタグの名前を変更するために使用できます。

次のステップは、新しいGitリポジトリを作成し、エクスポートスクリプトを実行することです

$ git init /tmp/converted
$ cd /tmp/converted
$ /tmp/fast-export/hg-fast-export.sh -r /tmp/hg-repo -A /tmp/authors

`-r`フラグは、変換したいMercurialリポジトリの場所をhg-fast-exportに伝え、`-A`フラグは、著者マッピングファイルの場所を伝えます(ブランチおよびタグマッピングファイルは、それぞれ`-B`および`-T`フラグで指定されます)。このスクリプトはMercurialのチェンジセットを解析し、Gitの「fast-import」機能(これについては後ほど詳しく説明します)用のスクリプトに変換します。これには少し時間がかかりますが(ネットワーク経由よりも*はるかに*高速です)、出力はかなり詳細です

$ /tmp/fast-export/hg-fast-export.sh -r /tmp/hg-repo -A /tmp/authors
Loaded 4 authors
master: Exporting full revision 1/22208 with 13/0/0 added/changed/removed files
master: Exporting simple delta revision 2/22208 with 1/1/0 added/changed/removed files
master: Exporting simple delta revision 3/22208 with 0/1/0 added/changed/removed files
[…]
master: Exporting simple delta revision 22206/22208 with 0/4/0 added/changed/removed files
master: Exporting simple delta revision 22207/22208 with 0/2/0 added/changed/removed files
master: Exporting thorough delta revision 22208/22208 with 3/213/0 added/changed/removed files
Exporting tag [0.4c] at [hg r9] [git :10]
Exporting tag [0.4d] at [hg r16] [git :17]
[…]
Exporting tag [3.1-rc] at [hg r21926] [git :21927]
Exporting tag [3.1] at [hg r21973] [git :21974]
Issued 22315 commands
git-fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects:     120000
Total objects:       115032 (    208171 duplicates                  )
      blobs  :        40504 (    205320 duplicates      26117 deltas of      39602 attempts)
      trees  :        52320 (      2851 duplicates      47467 deltas of      47599 attempts)
      commits:        22208 (         0 duplicates          0 deltas of          0 attempts)
      tags   :            0 (         0 duplicates          0 deltas of          0 attempts)
Total branches:         109 (         2 loads     )
      marks:        1048576 (     22208 unique    )
      atoms:           1952
Memory total:          7860 KiB
       pools:          2235 KiB
     objects:          5625 KiB
---------------------------------------------------------------------
pack_report: getpagesize()            =       4096
pack_report: core.packedGitWindowSize = 1073741824
pack_report: core.packedGitLimit      = 8589934592
pack_report: pack_used_ctr            =      90430
pack_report: pack_mmap_calls          =      46771
pack_report: pack_open_windows        =          1 /          1
pack_report: pack_mapped              =  340852700 /  340852700
---------------------------------------------------------------------

$ git shortlog -sn
   369  Bob Jones
   365  Joe Smith

これでほぼ完了です。すべてのMercurialタグはGitタグに変換され、MercurialのブランチとブックマークはGitブランチに変換されました。これで、リポジトリを新しいサーバー側のホームにプッシュする準備ができました

$ git remote add origin git@my-git-server:myrepository.git
$ git push origin --all

Perforce

次にインポートするシステムはPerforceです。上記で議論したように、GitとPerforceを連携させる方法には2つあります: git-p4とPerforce Git Fusionです。

Perforce Git Fusion

Git Fusionはこのプロセスをかなり簡単にしてくれます。設定ファイル(Git Fusionで説明したように)を使用してプロジェクト設定、ユーザーマッピング、およびブランチを設定し、リポジトリをクローンするだけです。Git Fusionは、ネイティブGitリポジトリのように見えるものを残し、必要であればネイティブGitホストにプッシュする準備が整います。希望すればPerforceをGitホストとして使用することもできます。

Git-p4

Git-p4はインポートツールとしても機能します。例として、Perforce Public DepotからJamプロジェクトをインポートします。クライアントをセットアップするには、P4PORT環境変数をPerforceデポを指すようにエクスポートする必要があります

$ export P4PORT=public.perforce.com:1666

手順に従うには、接続するためのPerforceデポが必要です。例ではpublic.perforce.comの公開デポを使用しますが、アクセスできる任意のデポを使用できます。

`git p4 clone`コマンドを実行して、PerforceサーバーからJamプロジェクトをインポートします。デポとプロジェクトパス、およびプロジェクトをインポートしたいパスを指定します

$ git-p4 clone //guest/perforce_software/jam@all p4import
Importing from //guest/perforce_software/jam@all into p4import
Initialized empty Git repository in /private/tmp/p4import/.git/
Import destination: refs/remotes/p4/master
Importing revision 9957 (100%)

この特定のプロジェクトには1つのブランチしかありませんが、ブランチビュー(または単なるディレクトリのセット)で設定されたブランチがある場合、`git p4 clone`に`--detect-branches`フラグを使用して、プロジェクトのすべてのブランチもインポートできます。これに関する詳細については、ブランチングを参照してください。

この時点でほぼ完了です。`p4import`ディレクトリに移動して`git log`を実行すると、インポートされた作業を確認できます

$ git log -2
commit e5da1c909e5db3036475419f6379f2c73710c4e6
Author: giles <giles@giles@perforce.com>
Date:   Wed Feb 8 03:13:27 2012 -0800

    Correction to line 355; change </UL> to </OL>.

    [git-p4: depot-paths = "//public/jam/src/": change = 8068]

commit aa21359a0a135dda85c50a7f7cf249e4f7b8fd98
Author: kwirth <kwirth@perforce.com>
Date:   Tue Jul 7 01:35:51 2009 -0800

    Fix spelling error on Jam doc page (cummulative -> cumulative).

    [git-p4: depot-paths = "//public/jam/src/": change = 7304]

`git-p4`が各コミットメッセージに識別子を残していることがわかります。後でPerforceの変更番号を参照する必要がある場合に備えて、その識別子をそこに保持しても問題ありません。ただし、識別子を削除したい場合は、新しいリポジトリで作業を開始する前に、今がその時です。`git filter-branch`を使用して、識別子文字列を一括で削除できます

$ git filter-branch --msg-filter 'sed -e "/^\[git-p4:/d"'
Rewrite e5da1c909e5db3036475419f6379f2c73710c4e6 (125/125)
Ref 'refs/heads/master' was rewritten

`git log`を実行すると、コミットのすべてのSHA-1チェックサムが変更されていることがわかりますが、`git-p4`文字列はコミットメッセージに存在しなくなっています。

$ git log -2
commit b17341801ed838d97f7800a54a6f9b95750839b7
Author: giles <giles@giles@perforce.com>
Date:   Wed Feb 8 03:13:27 2012 -0800

    Correction to line 355; change </UL> to </OL>.

commit 3e68c2e26cd89cb983eb52c024ecdfba1d6b3fff
Author: kwirth <kwirth@perforce.com>
Date:   Tue Jul 7 01:35:51 2009 -0800

    Fix spelling error on Jam doc page (cummulative -> cumulative).

インポートは新しいGitサーバーにプッシュする準備ができています。

カスタムインポーター

お使いのシステムが上記のいずれでもない場合は、オンラインでインポーターを探すべきです。CVS、Clear Case、Visual Source Safe、さらにはアーカイブのディレクトリなど、他の多くのシステム向けに高品質なインポーターが利用可能です。これらのツールのいずれも機能しない場合、より曖昧なツールを使用している場合、またはよりカスタムなインポートプロセスが必要な場合は、`git fast-import`を使用すべきです。このコマンドは、stdinから簡単な指示を読み取って特定のGitデータを書き込みます。生のGitコマンドを実行したり、生オブジェクトを書き込もうとするよりも、この方法でGitオブジェクトを作成する方がはるかに簡単です(詳細については、Gitの内部を参照してください)。このようにして、インポート元のシステムから必要な情報を読み取り、stdoutに直接的な指示を出力するインポートスクリプトを作成できます。その後、このプログラムを実行し、その出力を`git fast-import`にパイプできます。

簡単にデモンストレーションするために、シンプルなインポーターを作成します。`current`で作業しており、時々ディレクトリをタイムスタンプ付きの`back_YYYY_MM_DD`バックアップディレクトリにコピーしてプロジェクトをバックアップしているとします。そして、これをGitにインポートしたいとします。ディレクトリ構造は次のようになります

$ ls /opt/import_from
back_2014_01_02
back_2014_01_04
back_2014_01_14
back_2014_02_03
current

Gitディレクトリをインポートするには、Gitがデータをどのように保存しているかを確認する必要があります。ご存じのように、Gitは基本的に、コンテンツのスナップショットを指すコミットオブジェクトの連結リストです。必要なのは、`fast-import`にコンテンツのスナップショットが何であるか、どのコミットデータがそれらを指しているか、そしてそれらがどのような順序であるかを伝えることだけです。あなたの戦略は、スナップショットを1つずつ調べて、各ディレクトリの内容でコミットを作成し、各コミットを前のコミットにリンクすることです。

Gitによる強制ポリシーの例で示したように、私たちはこれをRubyで記述します。なぜなら、Rubyは私たちが一般的に扱っており、読みやすい傾向があるからです。この例は、あなたが慣れているどんな言語でもかなり簡単に記述できます。必要なのは、適切な情報を`stdout`に出力することだけです。そして、Windowsで実行している場合、行末にキャリッジリターンを導入しないように特別な注意を払う必要があります。`git fast-import`は、Windowsが使用するキャリッジリターンラインフィード(CRLF)ではなく、ラインフィード(LF)のみを非常に厳密に要求します。

まず、対象ディレクトリに移動し、各サブディレクトリ(それぞれコミットとしてインポートしたいスナップショット)を特定します。各サブディレクトリに移動し、それをエクスポートするために必要なコマンドを出力します。基本的なメインループは次のようになります

last_mark = nil

# loop through the directories
Dir.chdir(ARGV[0]) do
  Dir.glob("*").each do |dir|
    next if File.file?(dir)

    # move into the target directory
    Dir.chdir(dir) do
      last_mark = print_export(dir, last_mark)
    end
  end
end

各ディレクトリ内で`print_export`を実行します。これは、前のスナップショットのマニフェストとマークを受け取り、現在のスナップショットのマニフェストとマークを返します。これにより、適切にそれらをリンクできます。「マーク」は、コミットに与える識別子の`fast-import`用語です。コミットを作成する際、各コミットに他のコミットからリンクするために使用できるマークを与えます。したがって、`print_export`メソッドで最初に行うことは、ディレクトリ名からマークを生成することです

mark = convert_dir_to_mark(dir)

これは、ディレクトリの配列を作成し、インデックス値をマークとして使用することで行います。なぜなら、マークは整数でなければならないからです。あなたのメソッドは次のようになります

$marks = []
def convert_dir_to_mark(dir)
  if !$marks.include?(dir)
    $marks << dir
  end
  ($marks.index(dir) + 1).to_s
end

コミットの整数表現ができたので、コミットメタデータの日付が必要です。日付はディレクトリ名で表現されているため、それを解析します。`print_export`ファイルの次の行は次のとおりです

date = convert_dir_to_date(dir)

ここで、`convert_dir_to_date`は次のように定義されます

def convert_dir_to_date(dir)
  if dir == 'current'
    return Time.now().to_i
  else
    dir = dir.gsub('back_', '')
    (year, month, day) = dir.split('_')
    return Time.local(year, month, day).to_i
  end
end

これは各ディレクトリの日付の整数値を返します。各コミットに必要な最後のメタ情報は、コミッターデータで、これはグローバル変数にハードコードします

$author = 'John Doe <john@example.com>'

これで、インポーターのコミットデータの出力準備ができました。最初の情報では、コミットオブジェクトとそれがどのブランチにあるかを定義していること、続いて生成したマーク、コミッター情報とコミットメッセージ、そしてもしあれば前のコミットが示されています。コードは次のようになります

# print the import information
puts 'commit refs/heads/master'
puts 'mark :' + mark
puts "committer #{$author} #{date} -0700"
export_data('imported from ' + dir)
puts 'from :' + last_mark if last_mark

タイムゾーン(-0700)をハードコードするのは簡単だからです。他のシステムからインポートする場合は、タイムゾーンをオフセットとして指定する必要があります。コミットメッセージは特別な形式で表現する必要があります

data (size)\n(contents)

フォーマットは、`data`という単語、読み取るデータのサイズ、改行、そして最後にデータで構成されます。後でファイルの内容を指定するのに同じフォーマットを使用する必要があるため、ヘルパーメソッド`export_data`を作成します

def export_data(string)
  print "data #{string.size}\n#{string}"
end

残っているのは、各スナップショットのファイル内容を指定することだけです。これは簡単です。各スナップショットはディレクトリに保存されているため、`deleteall`コマンドの後にディレクトリ内の各ファイルのコンテンツを出力できます。するとGitは各スナップショットを適切に記録します

puts 'deleteall'
Dir.glob("**/*").each do |file|
  next if !File.file?(file)
  inline_data(file)
end

注: 多くのシステムがリビジョンをあるコミットから別のコミットへの変更として考えているため、fast-importは各コミットにコマンドを受け取り、追加、削除、または変更されたファイルを指定し、新しいコンテンツが何であるかを指定することもできます。スナップショット間の差分を計算し、このデータのみを提供することもできますが、そうするとより複雑になります。Gitにすべてのデータを与えて、Gitにそれを解釈させても良いでしょう。これがあなたのデータにより適している場合は、この方法でデータを提供する方法の詳細について`fast-import`のマニュアルページを確認してください。

新しいファイルの内容をリストアップしたり、新しい内容で変更されたファイルを指定したりする形式は次のとおりです

M 644 inline path/to/file
data (size)
(file contents)

ここで、644はモードです(実行可能ファイルがある場合は、検出して755を指定する必要があります)。そして`inline`は、この行の直後に内容をリストすることを意味します。あなたの`inline_data`メソッドは次のようになります

def inline_data(file, code = 'M', mode = '644')
  content = File.read(file)
  puts "#{code} #{mode} inline #{file}"
  export_data(content)
end

以前に定義した`export_data`メソッドを再利用します。これはコミットメッセージデータを指定した方法と同じだからです。

最後に行うべきことは、現在のマークを返して次のイテレーションに渡せるようにすることです

return mark

Windowsで実行している場合、追加で1つのステップを踏む必要があります。前述のとおり、Windowsは改行文字にCRLFを使用しますが、`git fast-import`はLFのみを期待します。この問題を回避し、`git fast-import`が正しく動作するようにするには、RubyにCRLFではなくLFを使用するように指示する必要があります

$stdout.binmode

以上です。スクリプト全体を次に示します

#!/usr/bin/env ruby

$stdout.binmode
$author = "John Doe <john@example.com>"

$marks = []
def convert_dir_to_mark(dir)
    if !$marks.include?(dir)
        $marks << dir
    end
    ($marks.index(dir)+1).to_s
end

def convert_dir_to_date(dir)
    if dir == 'current'
        return Time.now().to_i
    else
        dir = dir.gsub('back_', '')
        (year, month, day) = dir.split('_')
        return Time.local(year, month, day).to_i
    end
end

def export_data(string)
    print "data #{string.size}\n#{string}"
end

def inline_data(file, code='M', mode='644')
    content = File.read(file)
    puts "#{code} #{mode} inline #{file}"
    export_data(content)
end

def print_export(dir, last_mark)
    date = convert_dir_to_date(dir)
    mark = convert_dir_to_mark(dir)

    puts 'commit refs/heads/master'
    puts "mark :#{mark}"
    puts "committer #{$author} #{date} -0700"
    export_data("imported from #{dir}")
    puts "from :#{last_mark}" if last_mark

    puts 'deleteall'
    Dir.glob("**/*").each do |file|
        next if !File.file?(file)
        inline_data(file)
    end
    mark
end

# Loop through the directories
last_mark = nil
Dir.chdir(ARGV[0]) do
    Dir.glob("*").each do |dir|
        next if File.file?(dir)

        # move into the target directory
        Dir.chdir(dir) do
            last_mark = print_export(dir, last_mark)
        end
    end
end

このスクリプトを実行すると、次のようなコンテンツが得られます

$ ruby import.rb /opt/import_from
commit refs/heads/master
mark :1
committer John Doe <john@example.com> 1388649600 -0700
data 29
imported from back_2014_01_02deleteall
M 644 inline README.md
data 28
# Hello

This is my readme.
commit refs/heads/master
mark :2
committer John Doe <john@example.com> 1388822400 -0700
data 29
imported from back_2014_01_04from :1
deleteall
M 644 inline main.rb
data 34
#!/bin/env ruby

puts "Hey there"
M 644 inline README.md
(...)

インポーターを実行するには、インポート先のGitディレクトリでこの出力を`git fast-import`にパイプします。新しいディレクトリを作成し、そこで`git init`を実行して開始点とし、その後スクリプトを実行できます

$ git init
Initialized empty Git repository in /opt/import_to/.git/
$ ruby import.rb /opt/import_from | git fast-import
git-fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects:       5000
Total objects:           13 (         6 duplicates                  )
      blobs  :            5 (         4 duplicates          3 deltas of          5 attempts)
      trees  :            4 (         1 duplicates          0 deltas of          4 attempts)
      commits:            4 (         1 duplicates          0 deltas of          0 attempts)
      tags   :            0 (         0 duplicates          0 deltas of          0 attempts)
Total branches:           1 (         1 loads     )
      marks:           1024 (         5 unique    )
      atoms:              2
Memory total:          2344 KiB
       pools:          2110 KiB
     objects:           234 KiB
---------------------------------------------------------------------
pack_report: getpagesize()            =       4096
pack_report: core.packedGitWindowSize = 1073741824
pack_report: core.packedGitLimit      = 8589934592
pack_report: pack_used_ctr            =         10
pack_report: pack_mmap_calls          =          5
pack_report: pack_open_windows        =          2 /          2
pack_report: pack_mapped              =       1457 /       1457
---------------------------------------------------------------------

ご覧のとおり、正常に完了すると、達成されたことに関する多くの統計情報が表示されます。このケースでは、4つのコミットに対して合計13個のオブジェクトが1つのブランチにインポートされました。これで、`git log`を実行して新しい履歴を確認できます

$ git log -2
commit 3caa046d4aac682a55867132ccdfbe0d3fdee498
Author: John Doe <john@example.com>
Date:   Tue Jul 29 19:39:04 2014 -0700

    imported from current

commit 4afc2b945d0d3c8cd00556fbe2e8224569dc9def
Author: John Doe <john@example.com>
Date:   Mon Feb 3 01:00:00 2014 -0700

    imported from back_2014_02_03

これで、きれいで整ったGitリポジトリができました。最初は何もチェックアウトされていない、つまり作業ディレクトリにファイルがないことに注意することが重要です。それらを取得するには、ブランチを現在の`master`の位置にリセットする必要があります

$ ls
$ git reset --hard master
HEAD is now at 3caa046 imported from current
$ ls
README.md main.rb

`fast-import`ツールを使用すると、さらに多くのことができます。異なるモード、バイナリデータ、複数のブランチとマージ、タグ、進捗インジケータなどを処理できます。より複雑なシナリオの多くの例は、Gitソースコードの`contrib/fast-import`ディレクトリで入手できます。

scroll-to-top