チャプター ▾ 第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タグが除去されます。もちろん、これはgrepsort、および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を渡すことで、Subversionが通常インポートするメタデータを含めないようにgit svnに指示することもできます。メタデータには、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

Authorフィールドが見栄えが良くなっただけでなく、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は、そのブランチのpeg-revisionを指定するためにSVNで記述したのと同じように、SVNのバージョン番号をブランチ名に追加するだけです。もしpeg-revisionsが不要になった場合は、単純に削除してください。

$ 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シェルでワンラインコマンドです。

$ 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フラグはhg-fast-exportに変換したいMercurialリポジトリの場所を伝え、-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 depotを指すようにエクスポートする必要があります。

$ export P4PORT=public.perforce.com:1666

続けるには、接続するPerforce Depotが必要です。例ではpublic.perforce.comの公開Depotを使用しますが、アクセスできる任意のDepotを使用できます。

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を使用する必要があります。このコマンドは、特定のGitデータを書き込むためにstdinから簡単な指示を読み取ります。生の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で記述します。なぜなら、私たちが通常作業しているものであり、読みやすい傾向があるからです。この例は、あなたが慣れているものであれば何でも簡単に記述できます。必要なのは、適切な情報を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にすべてのデータを与えて、それに解決させる方がよいでしょう。もしこの方法がデータに適している場合は、fast-importのmanページで、この方法でデータを提供する方法の詳細を確認してください。

新しいファイル内容を列挙したり、変更されたファイルを新しい内容とともに指定したりする形式は次のとおりです。

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