-
1. はじめに
- 1.1 バージョン管理について
- 1.2 Gitの簡単な歴史
- 1.3 Gitとは?
- 1.4 コマンドライン
- 1.5 Gitのインストール
- 1.6 Gitの初回セットアップ
- 1.7 ヘルプの利用
- 1.8 まとめ
-
2. Gitの基本
- 2.1 Gitリポジトリの取得
- 2.2 リポジトリへの変更の記録
- 2.3 コミット履歴の表示
- 2.4 取り消し
- 2.5 リモートでの作業
- 2.6 タグ付け
- 2.7 Gitエイリアス
- 2.8 まとめ
-
3. Gitブランチ
- 3.1 ブランチの概要
- 3.2 基本的なブランチとマージ
- 3.3 ブランチ管理
- 3.4 ブランチワークフロー
- 3.5 リモートブランチ
- 3.6 リベース
- 3.7 まとめ
-
4. サーバー上のGit
- 4.1 プロトコル
- 4.2 サーバー上でのGitの取得
- 4.3 SSH公開鍵の生成
- 4.4 サーバーのセットアップ
- 4.5 Gitデーモン
- 4.6 スマートHTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 サードパーティホスト型オプション
- 4.10 まとめ
-
5. 分散Git
- 5.1 分散ワークフロー
- 5.2 プロジェクトへの貢献
- 5.3 プロジェクトの維持
- 5.4 まとめ
-
6. GitHub
- 6.1 アカウントのセットアップと構成
- 6.2 プロジェクトへの貢献
- 6.3 プロジェクトの維持
- 6.4 組織の管理
- 6.5 GitHubのスクリプト化
- 6.6 まとめ
-
7. Gitツール
- 7.1 リビジョンの選択
- 7.2 インタラクティブステージング
- 7.3 スタッシュとクリーン
- 7.4 作業の署名
- 7.5 検索
- 7.6 履歴の書き換え
- 7.7 リセットの解説
- 7.8 高度なマージ
- 7.9 Rerere
- 7.10 Gitによるデバッグ
- 7.11 サブモジュール
- 7.12 バンドリング
- 7.13 置換
- 7.14 認証情報の保存
- 7.15 まとめ
-
8. Gitのカスタマイズ
- 8.1 Gitの設定
- 8.2 Git属性
- 8.3 Gitフック
- 8.4 Git強制ポリシーの例
- 8.5 まとめ
-
9. Gitと他のシステム
- 9.1 クライアントとしてのGit
- 9.2 Gitへの移行
- 9.3 まとめ
-
10. Gitの内部構造
-
A1. 付録A:その他の環境でのGit
- A1.1 グラフィカルインターフェース
- A1.2 Visual StudioでのGit
- A1.3 Visual Studio CodeでのGit
- A1.4 IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMineでのGit
- A1.5 Sublime TextでのGit
- A1.6 BashでのGit
- A1.7 ZshでのGit
- A1.8 PowerShellでのGit
- A1.9 まとめ
-
A2. 付録B:アプリケーションへのGitの埋め込み
- A2.1 コマンドラインGit
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. 付録C:Gitコマンド
- A3.1 セットアップと設定
- A3.2 プロジェクトの取得と作成
- A3.3 基本的なスナップショット
- A3.4 ブランチとマージ
- A3.5 プロジェクトの共有と更新
- A3.6 検査と比較
- A3.7 デバッグ
- A3.8 パッチ
- A3.9 メール
- A3.10 外部システム
- A3.11 管理
- A3.12 プラミングコマンド
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
に提供して、作成者データをより正確にマップすることができます。また、git svn
に、通常Subversionがインポートするメタデータを含めないように指示することもできます。これには、clone
またはinit
コマンドに--no-metadata
を渡します。メタデータには、インポート中にGitが生成する各コミットメッセージ内のgit-svn-id
が含まれます。これにより、Gitログが肥大化し、少し不明瞭になる可能性があります。
注意
|
Gitリポジトリで行われたコミットを元のSVNリポジトリにミラーリングする場合は、メタデータを保持する必要があります。コミットログに同期が必要ない場合は、 |
これにより、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は数字)という接尾辞が付いた追加のブランチが表示される場合があります。これは実際にはSubversionの「ペグリビジョン」と呼ばれる機能で、Gitには構文上の対応物がありません。したがって、git svn
は、SVNでそのブランチのペグリビジョンを指定する場合と同じように、SVNバージョン番号をブランチ名に追加します。ペグリビジョンについてもう気にしない場合は、それらを削除するだけで済みます。
$ for p in $(git for-each-ref --format='%(refname:short)' | grep @); do git branch -D $p; done
これで、すべての古いブランチが実際のGitブランチになり、すべての古いタグが実際のGitタグになります。
最後にクリーンアップすることがもう1つあります。残念ながら、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の方が少し柔軟であるため、「hg-fast-export」というツールを使用して、MercurialからGitへのリポジトリの変換は非常に簡単です。このツールのコピーが必要です。
$ 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を使用すると、各行をルール:"<input>"="<output>"
に変換し、<input>
を<output>
にマッピングすることで、これを修正できます。<input>
と<output>
の文字列内では、Python string_escape
エンコーディングで理解されるすべてのエスケープシーケンスがサポートされています。作者マッピングファイルに一致する<input>
が含まれていない場合、その作者は変更されずに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の「高速インポート」機能(後で詳しく説明します)のスクリプトに変換します。これには少し時間がかかります(ただし、ネットワーク経由よりもはるかに高速です)。出力はかなり冗長です。
$ /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パブリックデポからJamプロジェクトをインポートします。クライアントを設定するには、Perforceデポを指すようにP4PORT環境変数をエクスポートする必要があります。
$ 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で記述します。この例は、使い慣れたものであれば、何でも非常に簡単に記述できます。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で実行している場合は、もう一つ追加のステップが必要になります。前述したように、Windowsでは改行文字にCRLFを使用しますが、
|
これで終わりです。スクリプト全体は次のとおりです。
#!/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
ディレクトリにあります。