-
1. Gitを始めるにあたって
- 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 Resetの解明
- 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の内側
- 10.1 PlumbingとPorcelain
- 10.2 Gitオブジェクト
- 10.3 Gitリファレンス
- 10.4 Packfile
- 10.5 Refspec
- 10.6 転送プロトコル
- 10.7 メンテナンスとデータ復旧
- 10.8 環境変数
- 10.9 まとめ
-
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 Plumbingコマンド
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
を渡すことで、Subversionが通常インポートするメタデータを含めないようにgit svn
に指示することもできます。メタデータには、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は数字) というサフィックスが付いた余分なブランチが見られることがあります。これは実際には "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を使用しますが、
|
それだけです。これがスクリプト全体です。
#!/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
ディレクトリに多数あります。