-
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 Rebase
- 3.7 まとめ
-
4. サーバー上のGit
- 4.1 プロトコル
- 4.2 サーバー上にGitを導入する
- 4.3 SSH公開鍵の生成
- 4.4 サーバーのセットアップ
- 4.5 Gitデーモン
- 4.6 Smart 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 Submodule
- 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 Plumbingコマンド
9.1 Gitとその他のシステム - クライアントとしてのGit
世の中は完璧ではありません。通常、接触するすべてのプロジェクトをすぐにGitに切り替えることはできません。時には、別のVCSを使用しているプロジェクトで行き詰まり、それがGitであればと願うことがあります。この章の最初の部分では、作業中のプロジェクトが別のシステムでホストされている場合に、Gitをクライアントとして使用する方法について学びます。
ある時点で、既存のプロジェクトをGitに変換したいと思うかもしれません。この章の後半では、特定のいくつかのシステムからGitにプロジェクトを移行する方法、そして事前に構築されたインポートツールが存在しない場合に機能する方法について説明します。
クライアントとしてのGit
Gitは開発者に非常に優れた体験を提供するため、チームの他のメンバーがまったく異なるVCSを使用していても、多くの人が自分のワークステーションでGitを使用する方法を見つけ出しました。これらの「ブリッジ」と呼ばれるアダプターがいくつか利用可能です。ここでは、最も遭遇する可能性のあるものを取り上げます。
GitとSubversion
オープンソース開発プロジェクトの大部分とかなりの数の企業プロジェクトが、ソースコードの管理にSubversionを使用しています。Subversionは10年以上前から存在し、そのほとんどの期間、オープンソースプロジェクトの事実上のVCS選択肢でした。また、それ以前のソース管理の世界で大物だったCVSと多くの点で非常に似ています。
Gitの優れた機能の1つは、git svn
と呼ばれるSubversionへの双方向ブリッジです。このツールを使用すると、GitをSubversionサーバーへの有効なクライアントとして使用できるため、Gitのすべてのローカル機能を使用し、まるでSubversionをローカルで使用しているかのようにSubversionサーバーにプッシュできます。これにより、共同作業者が古くからの方法で作業を続ける間も、ローカルでのブランチ作成とマージ、ステージングエリアの使用、リベースやチェリーピックなどを行うことができます。これは、Gitを企業環境にこっそり導入し、インフラストラクチャを完全にGitをサポートするように変更するよう働きかけながら、同僚の開発者の効率を向上させる良い方法です。Subversionブリッジは、DVCSの世界への入門薬です。
git svn
GitにおけるSubversionブリッジングコマンドの基本はgit svn
です。これにはかなりの数のコマンドがあるため、いくつかの簡単なワークフローを通して最も一般的なものを紹介します。
git svn
を使用する場合、Gitとは非常に異なる動作をするシステムであるSubversionとやり取りしていることに注意することが重要です。ローカルでのブランチ作成とマージは可能ですが、通常は作業をリベースすることで履歴を可能な限り線形に保ち、Gitリモートリポジトリと同時にやり取りするようなことは避けるのが最善です。
履歴を書き換えて再度プッシュしようとしたり、Git開発者と同時に共同作業するために並行するGitリポジトリにプッシュしたりしないでください。Subversionは単一の線形履歴しか持てず、これを混乱させるのは非常に簡単です。チームで作業していて、一部がSVNを使用し、他がGitを使用している場合は、全員がSVNサーバーを使用して共同作業するようにしてください。そうすることで、作業が楽になります。
セットアップ
この機能をデモンストレーションするには、書き込みアクセス権を持つ典型的なSVNリポジトリが必要です。これらの例をコピーしたい場合は、書き込み可能なSVNテストリポジトリのコピーを作成する必要があります。これを簡単に行うには、Subversionに付属しているsvnsync
というツールを使用できます。
手順に従うには、まず新しいローカルのSubversionリポジトリを作成する必要があります
$ mkdir /tmp/test-svn
$ svnadmin create /tmp/test-svn
次に、すべてのユーザーがrevpropを変更できるようにします。簡単な方法は、常に0を返すpre-revprop-change
スクリプトを追加することです
$ cat /tmp/test-svn/hooks/pre-revprop-change
#!/bin/sh
exit 0;
$ chmod +x /tmp/test-svn/hooks/pre-revprop-change
これで、svnsync init
をtoとfromのリポジトリを指定して呼び出すことで、このプロジェクトをローカルマシンに同期できます。
$ svnsync init file:///tmp/test-svn \
http://your-svn-server.example.org/svn/
これにより、同期を実行するためのプロパティが設定されます。その後、コードをクローンするには、以下を実行します
$ svnsync sync file:///tmp/test-svn
Committed revision 1.
Copied properties for revision 1.
Transmitting file data .............................[...]
Committed revision 2.
Copied properties for revision 2.
[…]
この操作には数分しかかからないかもしれませんが、元のリポジトリをローカルではなく別のリモートリポジトリにコピーしようとすると、コミットが100未満であっても、プロセスにほぼ1時間かかります。Subversionは一度に1つのリビジョンをクローンし、それを別のリポジトリにプッシュし直さなければなりません。これはとんでもなく非効率的ですが、これを行う唯一簡単な方法です。
始め方
これで書き込みアクセス権のあるSubversionリポジトリができたので、典型的なワークフローを進めることができます。まずgit svn clone
コマンドから始めます。これはSubversionリポジトリ全体をローカルのGitリポジトリにインポートします。実際のホストされているSubversionリポジトリからインポートする場合は、ここにあるfile:///tmp/test-svn
をSubversionリポジトリのURLに置き換える必要があることを忘れないでください
$ git svn clone file:///tmp/test-svn -T trunk -b branches -t tags
Initialized empty Git repository in /private/tmp/progit/test-svn/.git/
r1 = dcbfb5891860124cc2e8cc616cded42624897125 (refs/remotes/origin/trunk)
A m4/acx_pthread.m4
A m4/stl_hash.m4
A java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
A java/src/test/java/com/google/protobuf/WireFormatTest.java
…
r75 = 556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae (refs/remotes/origin/trunk)
Found possible branch point: file:///tmp/test-svn/trunk => file:///tmp/test-svn/branches/my-calc-branch, 75
Found branch parent: (refs/remotes/origin/my-calc-branch) 556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae
Following parent with do_switch
Successfully followed parent
r76 = 0fb585761df569eaecd8146c71e58d70147460a2 (refs/remotes/origin/my-calc-branch)
Checked out HEAD:
file:///tmp/test-svn/trunk r75
これは、指定されたURLに対してgit svn init
に続いてgit svn fetch
という2つのコマンドと同等の処理を実行します。これには時間がかかる場合があります。たとえば、テストプロジェクトのコミットが約75個しかなく、コードベースもそれほど大きくない場合でも、Gitは各バージョンを一度に1つずつチェックアウトし、個別にコミットする必要があります。数百または数千のコミットがあるプロジェクトの場合、これは文字通り数時間、あるいは数日かかることもあります。
-T trunk -b branches -t tags
の部分は、このSubversionリポジトリが基本的なブランチとタグの規則に従っていることをGitに伝えます。トランク、ブランチ、タグに異なる名前を付けている場合は、これらのオプションを変更できます。これが非常に一般的であるため、この部分全体を-s
(標準レイアウトを意味し、それらのすべてのオプションを含意します)に置き換えることができます。次のコマンドは同等です
$ git svn clone file:///tmp/test-svn -s
この時点で、ブランチとタグがインポートされた有効なGitリポジトリが作成されているはずです
$ git branch -a
* master
remotes/origin/my-calc-branch
remotes/origin/tags#2.0.2
remotes/origin/tags/release-2.0.1
remotes/origin/tags/release-2.0.2
remotes/origin/tags/release-2.0.2rc1
remotes/origin/trunk
このツールがSubversionタグをリモート参照として管理していることに注目してください。Gitのプラミングコマンドshow-ref
で詳しく見てみましょう
$ git show-ref
556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae refs/heads/master
0fb585761df569eaecd8146c71e58d70147460a2 refs/remotes/origin/my-calc-branch
bfd2d79303166789fc73af4046651a4b35c12f0b refs/remotes/origin/tags#2.0.2
285c2b2e36e467dd4d91c8e3c0c0e1750b3fe8ca refs/remotes/origin/tags/release-2.0.1
cbda99cb45d9abcb9793db1d4f70ae562a969f1e refs/remotes/origin/tags/release-2.0.2
a9f074aa89e826d6f9d30808ce5ae3ffe711feda refs/remotes/origin/tags/release-2.0.2rc1
556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae refs/remotes/origin/trunk
GitはGitサーバーからクローンする場合、このようにしません。タグのあるリポジトリが新しくクローンされた後はこのようになります
$ git show-ref
c3dcbe8488c6240392e8a5d7553bbffcb0f94ef0 refs/remotes/origin/master
32ef1d1c7cc8c603ab78416262cc421b80a8c2df refs/remotes/origin/branch-1
75f703a3580a9b81ead89fe1138e6da858c5ba18 refs/remotes/origin/branch-2
23f8588dde934e8f33c263c6d8359b2ae095f863 refs/tags/v0.1.0
7064938bd5e7ef47bfd79a685a62c1e2649e2ce7 refs/tags/v0.2.0
6dcb09b5b57875f334f61aebed695e2e4193db5e refs/tags/v1.0.0
Gitはタグをリモートブランチとして扱うのではなく、refs/tags
に直接フェッチします。
Subversionへのコミットバック
これで作業ディレクトリができたので、プロジェクトで作業を行い、Gitを効果的にSVNクライアントとして使用して、コミットを上流にプッシュできます。ファイルを編集してコミットすると、Subversionサーバーには存在しないローカルのGitにコミットが存在することになります
$ git commit -am 'Adding git-svn instructions to the README'
[master 4af61fd] Adding git-svn instructions to the README
1 file changed, 5 insertions(+)
次に、変更を上流にプッシュする必要があります。これによりSubversionとの作業方法がどのように変わるかに注目してください。オフラインで複数のコミットを行い、それらすべてを一度にSubversionサーバーにプッシュすることができます。Subversionサーバーにプッシュするには、git svn dcommit
コマンドを実行します
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
M README.txt
Committed r77
M README.txt
r77 = 95e0222ba6399739834380eb10afcd73e0670bc5 (refs/remotes/origin/trunk)
No changes between 4af61fd05045e07598c553167e0f31c84fd6ffe1 and refs/remotes/origin/trunk
Resetting to the latest refs/remotes/origin/trunk
これは、Subversionサーバーのコード上で行ったすべてのコミットを取り込み、それぞれに対してSubversionコミットを行い、その後、ローカルのGitコミットを一意の識別子を含むように書き換えます。これは、コミットのすべてのSHA-1チェックサムが変更されることを意味するため、重要です。一部にはこの理由により、Subversionサーバーと並行してGitベースのリモートバージョンのプロジェクトで作業するのは良い考えではありません。最後のコミットを見ると、追加された新しいgit-svn-id
を確認できます
$ git log -1
commit 95e0222ba6399739834380eb10afcd73e0670bc5
Author: ben <ben@0b684db3-b064-4277-89d1-21af03df0a68>
Date: Thu Jul 24 03:08:36 2014 +0000
Adding git-svn instructions to the README
git-svn-id: file:///tmp/test-svn/trunk@77 0b684db3-b064-4277-89d1-21af03df0a68
コミット時に4af61fd
で始まっていたSHA-1チェックサムが、今では95e0222
で始まっていることに注目してください。GitサーバーとSubversionサーバーの両方にプッシュしたい場合、その操作によってコミットデータが変更されるため、最初にSubversionサーバーにプッシュ(dcommit
)する必要があります。
新しい変更の取り込み
他の開発者と作業している場合、どこかの時点で誰かがプッシュし、その後別の誰かが競合する変更をプッシュしようとするでしょう。その変更は、相手の作業をマージするまで拒否されます。git svn
では、これは次のようになります
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
ERROR from SVN:
Transaction is out of date: File '/trunk/README.txt' is out of date
W: d5837c4b461b7c0e018b49d12398769d2bfc240a and refs/remotes/origin/trunk differ, using rebase:
:100644 100644 f414c433af0fd6734428cf9d2a9fd8ba00ada145 c80b6127dd04f5fcda218730ddf3a2da4eb39138 M README.txt
Current branch master is up to date.
ERROR: Not all changes have been committed into SVN, however the committed
ones (if any) seem to be successfully integrated into the working tree.
Please see the above messages for details.
この状況を解決するには、git svn rebase
を実行します。これは、サーバー上のまだ持っていない変更をすべてプルダウンし、自分の作業をサーバー上のものの上にリベースします
$ git svn rebase
Committing to file:///tmp/test-svn/trunk ...
ERROR from SVN:
Transaction is out of date: File '/trunk/README.txt' is out of date
W: eaa029d99f87c5c822c5c29039d19111ff32ef46 and refs/remotes/origin/trunk differ, using rebase:
:100644 100644 65536c6e30d263495c17d781962cfff12422693a b34372b25ccf4945fe5658fa381b075045e7702a M README.txt
First, rewinding head to replay your work on top of it...
Applying: update foo
Using index info to reconstruct a base tree...
M README.txt
Falling back to patching base and 3-way merge...
Auto-merging README.txt
ERROR: Not all changes have been committed into SVN, however the committed
ones (if any) seem to be successfully integrated into the working tree.
Please see the above messages for details.
これで、あなたのすべての作業がSubversionサーバー上のものの上に位置するようになったので、問題なくdcommit
できます
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
M README.txt
Committed r85
M README.txt
r85 = 9c29704cc0bbbed7bd58160cfb66cb9191835cd8 (refs/remotes/origin/trunk)
No changes between 5762f56732a958d6cfda681b661d2a239cc53ef5 and refs/remotes/origin/trunk
Resetting to the latest refs/remotes/origin/trunk
Gitではプッシュする前にローカルにない上流の作業をマージする必要がありますが、git svn
では変更が競合する場合にのみそれを行う必要があることに注意してください(Subversionの動作と非常に似ています)。誰かが1つのファイルに変更をプッシュし、その後あなたが別のファイルに変更をプッシュした場合、あなたのdcommit
は問題なく動作します
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
M configure.ac
Committed r87
M autogen.sh
r86 = d8450bab8a77228a644b7dc0e95977ffc61adff7 (refs/remotes/origin/trunk)
M configure.ac
r87 = f3653ea40cb4e26b6281cec102e35dcba1fe17c4 (refs/remotes/origin/trunk)
W: a0253d06732169107aa020390d9fefd2b1d92806 and refs/remotes/origin/trunk differ, using rebase:
:100755 100755 efa5a59965fbbb5b2b0a12890f1b351bb5493c18 e757b59a9439312d80d5d43bb65d4a7d0389ed6d M autogen.sh
First, rewinding head to replay your work on top of it...
これは覚えておくべき重要な点です。なぜなら、結果として得られるプロジェクトの状態は、プッシュした時点でどちらのコンピューターにも存在しなかったからです。変更が互換性がないが競合しない場合、診断が難しい問題が発生する可能性があります。これはGitサーバーを使用する場合とは異なります。Gitでは、公開する前にクライアントシステムで状態を完全にテストできますが、SVNでは、コミット直前とコミット直後の状態が同一であると確信することはできません。
コミットする準備ができていない場合でも、Subversionサーバーから変更をプルするためにこのコマンドを実行する必要があります。新しいデータを取得するにはgit svn fetch
を実行できますが、git svn rebase
はフェッチを実行し、その後ローカルのコミットを更新します。
$ git svn rebase
M autogen.sh
r88 = c9c5f83c64bd755368784b444bc7a0216cc1e17b (refs/remotes/origin/trunk)
First, rewinding head to replay your work on top of it...
Fast-forwarded master to refs/remotes/origin/trunk.
時々git svn rebase
を実行することで、コードが常に最新であることを確認できます。ただし、これを実行する際には作業ディレクトリがクリーンであることを確認する必要があります。ローカルに変更がある場合、git svn rebase
を実行する前に作業をスタッシュするか、一時的にコミットする必要があります。そうしないと、リベースがマージコンフリクトを引き起こすことが検出された場合、コマンドは停止します。
Gitブランチの問題
Gitワークフローに慣れてくると、トピックブランチを作成し、そこで作業を行い、その後それらをマージすることがよくあります。git svn
を介してSubversionサーバーにプッシュする場合、ブランチをマージするのではなく、毎回自分の作業を単一のブランチにリベースすることをお勧めします。リベースを好む理由は、Subversionが線形履歴を持ち、Gitのようにマージを扱わないため、git svn
はスナップショットをSubversionコミットに変換する際に最初の親のみを追跡するためです。
履歴が次のようになっているとします。experiment
ブランチを作成し、2つのコミットを行い、その後それらをmaster
にマージし直しました。dcommit
を実行すると、次のような出力が表示されます
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
M CHANGES.txt
Committed r89
M CHANGES.txt
r89 = 89d492c884ea7c834353563d5d913c6adf933981 (refs/remotes/origin/trunk)
M COPYING.txt
M INSTALL.txt
Committed r90
M INSTALL.txt
M COPYING.txt
r90 = cb522197870e61467473391799148f6721bcf9a0 (refs/remotes/origin/trunk)
No changes between 71af502c214ba13123992338569f4669877f55fd and refs/remotes/origin/trunk
Resetting to the latest refs/remotes/origin/trunk
マージ履歴を持つブランチでdcommit
を実行しても問題なく機能しますが、Gitプロジェクトの履歴を見ると、experiment
ブランチで行ったコミットのいずれも書き換えられていません。代わりに、それらのすべての変更は単一のマージコミットのSVNバージョンに表示されます。
他の誰かがその作業をクローンすると、彼らが見るのは、あたかもgit merge --squash
を実行したかのように、すべての作業が1つにまとめられたマージコミットだけです。そのコミットがどこから来たのか、いつコミットされたのかについてのコミットデータは表示されません。
Subversionのブランチ機能
Subversionでのブランチ作成はGitでのブランチ作成とは異なります。あまり使用せずに済むのであれば、それがおそらく最善です。ただし、git svn
を使用してSubversionでブランチを作成し、コミットすることは可能です。
新しいSVNブランチの作成
Subversionで新しいブランチを作成するには、git svn branch [new-branch]
を実行します
$ git svn branch opera
Copying file:///tmp/test-svn/trunk at r90 to file:///tmp/test-svn/branches/opera...
Found possible branch point: file:///tmp/test-svn/trunk => file:///tmp/test-svn/branches/opera, 90
Found branch parent: (refs/remotes/origin/opera) cb522197870e61467473391799148f6721bcf9a0
Following parent with do_switch
Successfully followed parent
r91 = f1b64a3855d3c8dd84ee0ef10fa89d27f1584302 (refs/remotes/origin/opera)
これはSubversionのsvn copy trunk branches/opera
コマンドと同等のことを行い、Subversionサーバー上で動作します。重要なのは、このブランチにチェックアウトされるわけではないということです。この時点でコミットすると、そのコミットはサーバー上のtrunk
に行き、opera
には行きません。
アクティブブランチの切り替え
Gitは、あなたの履歴にあるSubversionブランチの先端を探すことで、どのブランチにdcommitが送られるかを判断します。通常は1つだけで、現在のブランチ履歴でgit-svn-id
を持つ最後のものになるはずです。
複数のブランチで同時に作業したい場合は、そのブランチのインポートされたSubversionコミットから開始することで、特定のSubversionブランチにdcommit
するローカルブランチを設定できます。個別に作業できるopera
ブランチが必要な場合は、次のように実行できます
$ git branch opera remotes/origin/opera
これで、opera
ブランチをtrunk
(あなたのmaster
ブランチ)にマージしたい場合、通常のgit merge
で行うことができます。ただし、分かりやすいコミットメッセージ(-m
を使用)を指定する必要があります。そうしないと、マージメッセージが「Merge branch opera」となってしまい、有用な情報が得られません。
この操作にgit merge
を使用しているとはいえ、マージはSubversionで行うよりもはるかに簡単になる可能性が高いですが(Gitが適切なマージベースを自動的に検出してくれるため)、これは通常のGitマージコミットではないことを覚えておいてください。このデータを、複数の親を追跡するコミットを処理できないSubversionサーバーにプッシュしなければなりません。そのため、プッシュした後には、まるで別のブランチのすべての作業が単一のコミットに圧縮されたかのような単一のコミットに見えるでしょう。あるブランチを別のブランチにマージした後、Gitで通常できるような方法で、簡単に戻ってそのブランチで作業を続けることはできません。実行するdcommit
コマンドは、どのブランチがマージされたかを示す情報を消去するため、その後のマージベース計算が誤りになります。dcommit
は、git merge
の結果をあたかもgit merge --squash
を実行したかのように見せます。残念ながら、この状況を回避する良い方法はありません。Subversionはこの情報を保存できないため、サーバーとして使用している間は常にその制限によって制約を受けることになります。問題を避けるため、ブランチ(この場合はopera
)をトランクにマージした後、そのローカルブランチを削除するべきです。
Subversionコマンド
git svn
ツールセットは、Subversionにあった機能と類似の機能を提供することで、Gitへの移行を容易にするための多くのコマンドを提供します。以下に、Subversionが提供していたものと同様の機能を提供するいくつかのコマンドを紹介します。
SVNスタイルの履歴
Subversionに慣れていて、SVN出力スタイルで履歴を見たい場合は、git svn log
を実行してSVN形式でコミット履歴を表示できます
$ git svn log
------------------------------------------------------------------------
r87 | schacon | 2014-05-02 16:07:37 -0700 (Sat, 02 May 2014) | 2 lines
autogen change
------------------------------------------------------------------------
r86 | schacon | 2014-05-02 16:00:21 -0700 (Sat, 02 May 2014) | 2 lines
Merge branch 'experiment'
------------------------------------------------------------------------
r85 | schacon | 2014-05-02 16:00:09 -0700 (Sat, 02 May 2014) | 2 lines
updated the changelog
git svn log
について知っておくべき重要な点が2つあります。まず、実際のsvn log
コマンドがSubversionサーバーにデータを要求するのとは異なり、オフラインで動作します。次に、Subversionサーバーにコミットされたコミットのみが表示されます。あなたがdcommitしていないローカルのGitコミットは表示されませんし、その間に他の人がSubversionサーバーに行ったコミットも表示されません。これはSubversionサーバー上のコミットの最後の既知の状態に近いものです。
SVNアノテーション
git svn log
コマンドがsvn log
コマンドをオフラインでシミュレートするのと同様に、git svn blame [FILE]
を実行することでsvn annotate
に相当するものを取得できます。出力は次のようになります
$ git svn blame README.txt
2 temporal Protocol Buffers - Google's data interchange format
2 temporal Copyright 2008 Google Inc.
2 temporal http://code.google.com/apis/protocolbuffers/
2 temporal
22 temporal C++ Installation - Unix
22 temporal =======================
2 temporal
79 schacon Committing in git-svn.
78 schacon
2 temporal To build and install the C++ Protocol Buffer runtime and the Protocol
2 temporal Buffer compiler (protoc) execute the following:
2 temporal
繰り返しますが、Gitでローカルに行ったコミットや、その間にSubversionにプッシュされたコミットは表示されません。
SVNサーバー情報
git svn info
を実行することで、svn info
が提供するのと同種の情報を取得できます
$ git svn info
Path: .
URL: https://schacon-test.googlecode.com/svn/trunk
Repository Root: https://schacon-test.googlecode.com/svn
Repository UUID: 4c93b258-373f-11de-be05-5f7a86268029
Revision: 87
Node Kind: directory
Schedule: normal
Last Changed Author: schacon
Last Changed Rev: 87
Last Changed Date: 2009-05-02 16:07:37 -0700 (Sat, 02 May 2009)
これはblame
やlog
と同様に、オフラインで実行され、Subversionサーバーと最後に通信した時点のデータのみが最新の状態です。
Subversionが無視するものを無視する
svn:ignore
プロパティがどこかに設定されているSubversionリポジトリをクローンした場合、誤ってコミットすべきでないファイルをコミットしないように、対応する.gitignore
ファイルを設定したいと思うでしょう。git svn
には、この問題を解決するための2つのコマンドがあります。1つ目はgit svn create-ignore
で、これは対応する.gitignore
ファイルを自動的に作成し、次のコミットに含めることができます。
2つ目のコマンドはgit svn show-ignore
で、これは.gitignore
ファイルに含める必要がある行を標準出力に表示するため、その出力をプロジェクトの除外ファイルにリダイレクトできます
$ git svn show-ignore > .git/info/exclude
そうすることで、プロジェクトを.gitignore
ファイルで散らかすことがなくなります。これは、Subversionチームであなたが唯一のGitユーザーであり、チームメイトがプロジェクトに.gitignore
ファイルを望まない場合に良い選択肢です。
Git-SVNのまとめ
git svn
ツールは、Subversionサーバーに縛られている場合や、Subversionサーバーの実行が必須となる開発環境にいる場合に便利です。しかし、それを不完全なGitと考えるべきです。そうしないと、翻訳の問題であなたと共同作業者を混乱させる可能性があります。問題を起こさないためには、以下のガイドラインに従うようにしてください
-
git merge
によって作成されたマージコミットを含まない線形なGit履歴を保ちます。メインラインブランチ外で行った作業はすべてその上にリベースしてください。マージしないでください。 -
別のGitサーバーをセットアップして共同作業しないでください。新しい開発者のクローンを高速化するために1つ持つことは可能ですが、
git-svn-id
エントリを持たないものをプッシュしないでください。各コミットメッセージにgit-svn-id
があるかを確認し、それがないコミットを含むプッシュを拒否するpre-receive
フックを追加することも検討してください。
これらのガイドラインに従えば、Subversionサーバーとの作業はより耐えやすくなるでしょう。しかし、もし真のGitサーバーに移行できるのであれば、そうすることでチームはより多くの利益を得ることができます。
GitとMercurial
DVCSの世界はGitだけではありません。実際、この分野には他にも多くのシステムがあり、それぞれが分散バージョン管理を正しく行うための独自の視点を持っています。Git以外で最も人気があるのはMercurialであり、この2つは多くの点で非常に似ています。
Gitのクライアント側の動作を好むものの、Mercurialでソースコードが管理されているプロジェクトで作業している場合の良いニュースは、MercurialでホストされているリポジトリのクライアントとしてGitを使用する方法があるということです。Gitがサーバーリポジトリと通信する方法はリモートを介しているため、このブリッジがリモートヘルパーとして実装されていることは驚くにはあたりません。プロジェクト名はgit-remote-hgで、https://github.com/felipec/git-remote-hgで見つけることができます。
git-remote-hg
まず、git-remote-hgをインストールする必要があります。これは基本的に、そのファイルをパスのどこかに置くことを意味します。例えば、次のようになります
$ curl -o ~/bin/git-remote-hg \
https://raw.githubusercontent.com/felipec/git-remote-hg/master/git-remote-hg
$ chmod +x ~/bin/git-remote-hg
…~/bin
があなたの$PATH
にあると仮定します。Git-remote-hgにはもう1つの依存関係があります。Python用のmercurial
ライブラリです。Pythonがインストールされていれば、これは次のように簡単です
$ pip install mercurial
Pythonがインストールされていない場合は、まずhttps://www.python.org/にアクセスして入手してください。
最後に必要なのはMercurialクライアントです。まだインストールしていない場合は、https://www.mercurial-scm.org/にアクセスしてインストールしてください。
これで準備万端です。必要なのは、プッシュできるMercurialリポジトリだけです。幸いなことに、すべてのMercurialリポジトリはこのように機能するため、Mercurialを学ぶ誰もが使う「Hello World」リポジトリを使用します
$ hg clone http://selenic.com/repo/hello /tmp/hello
始め方
これで適切な「サーバーサイド」リポジトリができたので、典型的なワークフローを試すことができます。ご覧のとおり、これら2つのシステムは十分似ており、ほとんど摩擦がありません。
Gitではいつも通り、まずクローンします
$ git clone hg::/tmp/hello /tmp/hello-git
$ cd /tmp/hello-git
$ git log --oneline --graph --decorate
* ac7955c (HEAD, origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master, master) Create a makefile
* 65bb417 Create a standard 'hello, world' program
Mercurialリポジトリを操作する際には、標準のgit clone
コマンドを使用することに気づくでしょう。これは、git-remote-hgがGitのHTTP/Sプロトコルが実装されている方法(リモートヘルパー)と似たメカニズムを使用して、かなり低いレベルで動作しているためです。GitとMercurialの両方が、すべてのクライアントがリポジトリ履歴の完全なコピーを持つように設計されているため、このコマンドはプロジェクトの履歴全体を含む完全なクローンを作成し、それをかなり迅速に行います。
log
コマンドは2つのコミットを表示しますが、その最新のものは多くのrefにポイントされています。実際にはこれらのいくつかは存在しません。.git
ディレクトリに実際に何があるかを見てみましょう
$ tree .git/refs
.git/refs
├── heads
│ └── master
├── hg
│ └── origin
│ ├── bookmarks
│ │ └── master
│ └── branches
│ └── default
├── notes
│ └── hg
├── remotes
│ └── origin
│ └── HEAD
└── tags
9 directories, 5 files
Git-remote-hgは物事をよりGitらしい慣用表現にしようとしていますが、内部的には2つのわずかに異なるシステム間の概念的なマッピングを管理しています。refs/hg
ディレクトリは実際のリモート参照が格納される場所です。例えば、refs/hg/origin/branches/default
は「ac7955c」で始まるSHA-1を含むGit参照ファイルであり、これはmaster
が指すコミットです。したがって、refs/hg
ディレクトリは、偽のrefs/remotes/origin
のようなものですが、ブックマークとブランチの間の区別が追加されています。
notes/hg
ファイルは、git-remote-hgがGitコミットハッシュをMercurialチェンジセットIDにマッピングする方法の出発点です。少し掘り下げてみましょう
$ cat notes/hg
d4c10386...
$ git cat-file -p d4c10386...
tree 1781c96...
author remote-hg <> 1408066400 -0800
committer remote-hg <> 1408066400 -0800
Notes for master
$ git ls-tree 1781c96...
100644 blob ac9117f... 65bb417...
100644 blob 485e178... ac7955c...
$ git cat-file -p ac9117f
0a04b987be5ae354b710cefeba0e2d9de7ad41a9
つまり、refs/notes/hg
はツリーを指しており、これはGitオブジェクトデータベースでは名前を持つ他のオブジェクトのリストです。git ls-tree
はツリー内のアイテムのモード、タイプ、オブジェクトハッシュ、ファイル名を出力します。ツリーアイテムの1つを深く掘り下げてみると、その中に「ac9117f」(master
が指すコミットのSHA-1ハッシュ)という名前のblobがあり、内容は「0a04b98」(default
ブランチの先端にあるMercurialチェンジセットのID)であることがわかります。
良いニュースは、これらすべてについてほとんど心配する必要がないということです。典型的なワークフローはGitリモートとの作業と大きく変わりません。
続ける前に、もう一つ対処すべきことがあります。それは「無視」です。MercurialとGitはこれに非常に似たメカニズムを使用していますが、Mercurialリポジトリに実際に.gitignore
ファイルをコミットしたくない場合が多いでしょう。幸いなことに、Gitにはディスク上のリポジトリにローカルなファイルを無視する方法があり、Mercurialの形式はGitと互換性があるため、単にコピーするだけで済みます
$ cp .hgignore .git/info/exclude
.git/info/exclude
ファイルは.gitignore
とまったく同じように動作しますが、コミットには含まれません。
ワークフロー
master
ブランチで作業を完了し、いくつかのコミットを行ったとしましょう。そして、それをリモートリポジトリにプッシュする準備ができています。現在のリポジトリの状態は次のとおりです
$ git log --oneline --graph --decorate
* ba04a2a (HEAD, master) Update makefile
* d25d16f Goodbye
* ac7955c (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Create a makefile
* 65bb417 Create a standard 'hello, world' program
master
ブランチはorigin/master
より2コミット進んでいますが、これらの2つのコミットはローカルマシン上にのみ存在します。同時に他の誰かが重要な作業をしていたかどうか見てみましょう
$ git fetch
From hg::/tmp/hello
ac7955c..df85e87 master -> origin/master
ac7955c..df85e87 branches/default -> origin/branches/default
$ git log --oneline --graph --decorate --all
* 7b07969 (refs/notes/hg) Notes for default
* d4c1038 Notes for master
* df85e87 (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Add some documentation
| * ba04a2a (HEAD, master) Update makefile
| * d25d16f Goodbye
|/
* ac7955c Create a makefile
* 65bb417 Create a standard 'hello, world' program
--all
フラグを使用したため、git-remote-hgによって内部的に使用される「notes」参照が表示されますが、これらは無視できます。残りは予想通りです。origin/master
は1コミット進んでおり、私たちの履歴は分岐しました。この章で扱う他のシステムとは異なり、Mercurialはマージを処理できるため、特別なことは何も行いません。
$ git merge origin/master
Auto-merging hello.c
Merge made by the 'recursive' strategy.
hello.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git log --oneline --graph --decorate
* 0c64627 (HEAD, master) Merge remote-tracking branch 'origin/master'
|\
| * df85e87 (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Add some documentation
* | ba04a2a Update makefile
* | d25d16f Goodbye
|/
* ac7955c Create a makefile
* 65bb417 Create a standard 'hello, world' program
完璧です。テストを実行するとすべてパスしたので、チームの残りのメンバーと作業を共有する準備ができました
$ git push
To hg::/tmp/hello
df85e87..0c64627 master -> master
以上です!Mercurialリポジトリを見てみると、予想通りのことが行われたことがわかるでしょう
$ hg log -G --style compact
o 5[tip]:4,2 dc8fa4f932b8 2014-08-14 19:33 -0700 ben
|\ Merge remote-tracking branch 'origin/master'
| |
| o 4 64f27bcefc35 2014-08-14 19:27 -0700 ben
| | Update makefile
| |
| o 3:1 4256fc29598f 2014-08-14 19:27 -0700 ben
| | Goodbye
| |
@ | 2 7db0b4848b3c 2014-08-14 19:30 -0700 ben
|/ Add some documentation
|
o 1 82e55d328c8c 2005-08-26 01:21 -0700 mpm
| Create a makefile
|
o 0 0a04b987be5a 2005-08-26 01:20 -0700 mpm
Create a standard 'hello, world' program
番号2の変更セットはMercurialによって作成され、番号3と4の変更セットはgit-remote-hgによって、Gitで行われたコミットをプッシュすることで作成されました。
ブランチとブックマーク
Gitには1種類のブランチしかありません。それは、コミットが行われると移動する参照です。Mercurialでは、この種の参照は「ブックマーク」と呼ばれ、Gitブランチと非常によく似た動作をします。
Mercurialの「ブランチ」の概念は、より重いです。変更セットが作成されたブランチは、変更セットとともに記録されるため、常にリポジトリ履歴に残ります。以下はdevelop
ブランチで作成されたコミットの例です
$ hg log -l 1
changeset: 6:8f65e5e02793
branch: develop
tag: tip
user: Ben Straub <ben@straub.cc>
date: Thu Aug 14 20:06:38 2014 -0700
summary: More documentation
「branch」で始まる行に注目してください。Gitはこれを完全に再現することはできません(そして、その必要もありません。どちらの種類のブランチもGitの参照として表現できます)が、Mercurialが気にするため、git-remote-hgはこの違いを理解する必要があります。
Mercurialのブックマークを作成するのは、Gitブランチを作成するのと同じくらい簡単です。Git側では
$ git checkout -b featureA
Switched to a new branch 'featureA'
$ git push origin featureA
To hg::/tmp/hello
* [new branch] featureA -> featureA
それだけです。Mercurial側では、次のようになります
$ hg bookmarks
featureA 5:bd5ac26f11f9
$ hg log --style compact -G
@ 6[tip] 8f65e5e02793 2014-08-14 20:06 -0700 ben
| More documentation
|
o 5[featureA]:4,2 bd5ac26f11f9 2014-08-14 20:02 -0700 ben
|\ Merge remote-tracking branch 'origin/master'
| |
| o 4 0434aaa6b91f 2014-08-14 20:01 -0700 ben
| | update makefile
| |
| o 3:1 318914536c86 2014-08-14 20:00 -0700 ben
| | goodbye
| |
o | 2 f098c7f45c4f 2014-08-14 20:01 -0700 ben
|/ Add some documentation
|
o 1 82e55d328c8c 2005-08-26 01:21 -0700 mpm
| Create a makefile
|
o 0 0a04b987be5a 2005-08-26 01:20 -0700 mpm
Create a standard 'hello, world' program
リビジョン5に新しい[featureA]
タグがあることに注目してください。これらはGit側ではGitブランチとまったく同じように動作しますが、1つ例外があります。Git側からブックマークを削除することはできません(これはリモートヘルパーの制限です)。
「ヘビーウェイト」なMercurialブランチでも作業できます。branches
名前空間にブランチを配置するだけです
$ git checkout -b branches/permanent
Switched to a new branch 'branches/permanent'
$ vi Makefile
$ git commit -am 'A permanent change'
$ git push origin branches/permanent
To hg::/tmp/hello
* [new branch] branches/permanent -> branches/permanent
Mercurial側では次のようになります
$ hg branches
permanent 7:a4529d07aad4
develop 6:8f65e5e02793
default 5:bd5ac26f11f9 (inactive)
$ hg log -G
o changeset: 7:a4529d07aad4
| branch: permanent
| tag: tip
| parent: 5:bd5ac26f11f9
| user: Ben Straub <ben@straub.cc>
| date: Thu Aug 14 20:21:09 2014 -0700
| summary: A permanent change
|
| @ changeset: 6:8f65e5e02793
|/ branch: develop
| user: Ben Straub <ben@straub.cc>
| date: Thu Aug 14 20:06:38 2014 -0700
| summary: More documentation
|
o changeset: 5:bd5ac26f11f9
|\ bookmark: featureA
| | parent: 4:0434aaa6b91f
| | parent: 2:f098c7f45c4f
| | user: Ben Straub <ben@straub.cc>
| | date: Thu Aug 14 20:02:21 2014 -0700
| | summary: Merge remote-tracking branch 'origin/master'
[...]
ブランチ名「permanent」は、番号7の変更セットとともに記録されました。
Git側から見ると、これらのブランチスタイルのどちらを使用しても作業は同じです。通常どおりチェックアウト、コミット、フェッチ、マージ、プル、プッシュを行うだけです。知っておくべきことの1つは、Mercurialが履歴の書き換えをサポートしておらず、追加のみであるということです。以下は、インタラクティブなリベースと強制プッシュを行った後のMercurialリポジトリの状態です
$ hg log --style compact -G
o 10[tip] 99611176cbc9 2014-08-14 20:21 -0700 ben
| A permanent change
|
o 9 f23e12f939c3 2014-08-14 20:01 -0700 ben
| Add some documentation
|
o 8:1 c16971d33922 2014-08-14 20:00 -0700 ben
| goodbye
|
| o 7:5 a4529d07aad4 2014-08-14 20:21 -0700 ben
| | A permanent change
| |
| | @ 6 8f65e5e02793 2014-08-14 20:06 -0700 ben
| |/ More documentation
| |
| o 5[featureA]:4,2 bd5ac26f11f9 2014-08-14 20:02 -0700 ben
| |\ Merge remote-tracking branch 'origin/master'
| | |
| | o 4 0434aaa6b91f 2014-08-14 20:01 -0700 ben
| | | update makefile
| | |
+---o 3:1 318914536c86 2014-08-14 20:00 -0700 ben
| | goodbye
| |
| o 2 f098c7f45c4f 2014-08-14 20:01 -0700 ben
|/ Add some documentation
|
o 1 82e55d328c8c 2005-08-26 01:21 -0700 mpm
| Create a makefile
|
o 0 0a04b987be5a 2005-08-26 01:20 -0700 mpm
Create a standard "hello, world" program
変更セット8、9、および10は作成され、permanent
ブランチに属していますが、古い変更セットはまだ存在します。これはMercurialを使用しているチームメイトにとって非常に混乱を招く可能性があるため、避けるようにしてください。
Mercurialのまとめ
GitとMercurialは十分に似ているため、境界を越えて作業してもほとんど苦痛はありません。マシンを離れた履歴を変更しないようにすれば(一般的に推奨されています)、相手側がMercurialであることさえ気づかないかもしれません。
GitとPerforce
Perforceは企業環境で非常に人気のあるバージョン管理システムです。1995年から存在しており、この章で扱うシステムの中では最も古いものです。そのため、当時の制約を考慮して設計されており、常に単一の中央サーバーに接続し、ローカルディスクには1つのバージョンのみが保持されることを前提としています。確かに、その機能と制約はいくつかの特定の課題によく適していますが、Gitの方が実際にはより良く機能するであろうPerforceを使用しているプロジェクトも多く存在します。
PerforceとGitの使用を混在させたい場合、2つの選択肢があります。最初の選択肢は、Perforceのメーカーが提供する「Git Fusion」ブリッジで、Perforceデポのサブツリーを読み書き可能なGitリポジトリとして公開できます。2番目はgit-p4で、Perforceサーバーの設定を一切変更することなく、GitをPerforceクライアントとして使用できるクライアントサイドのブリッジです。
Git Fusion
Perforceは、Git Fusion(https://www.perforce.com/manuals/git-fusion/で入手可能)と呼ばれる製品を提供しています。これは、Perforceサーバーとサーバー側のGitリポジトリを同期します。
セットアップ
例として、Git Fusionの最も簡単なインストール方法である、PerforceデーモンとGit Fusionを実行する仮想マシンをダウンロードする方法を使用します。https://www.perforce.com/downloadsから仮想マシンイメージを入手でき、ダウンロードが完了したら、お気に入りの仮想化ソフトウェア(ここではVirtualBoxを使用します)にインポートします。
マシンを最初に起動すると、3つのLinuxユーザー(root
、perforce
、git
)のパスワードをカスタマイズし、同じネットワーク上の他のインストールと区別するために使用できるインスタンス名を提供するように求められます。これらすべてが完了すると、次の画面が表示されます

ここに表示されているIPアドレスをメモしておいてください。後でこれを使用します。次に、Perforceユーザーを作成します。下部の「Login」オプションを選択してEnterキーを押し(またはマシンにSSH接続し)、root
としてログインします。その後、以下のコマンドを使用してユーザーを作成します
$ p4 -p localhost:1666 -u super user -f john
$ p4 -p localhost:1666 -u john passwd
$ exit
最初のコマンドはVIエディターを開いてユーザーをカスタマイズしますが、:wq
と入力してEnterキーを押すことでデフォルトを受け入れることができます。2番目のコマンドはパスワードを2回入力するように求めます。シェルプロンプトでの作業はこれだけなので、セッションを終了してください。
次に、GitにSSL証明書を検証しないように指示する必要があります。Git Fusionイメージには証明書が付属していますが、これは仮想マシンのIPアドレスと一致しないドメインのものであるため、GitはHTTPS接続を拒否します。これが恒久的なインストールになる場合は、異なる証明書をインストールするためにPerforce Git Fusionマニュアルを参照してください。今回の例では、これで十分です
$ export GIT_SSL_NO_VERIFY=true
これで、すべてが機能しているかテストできます。
$ git clone https://10.0.1.254/Talkhouse
Cloning into 'Talkhouse'...
Username for 'https://10.0.1.254': john
Password for 'https://john@10.0.1.254':
remote: Counting objects: 630, done.
remote: Compressing objects: 100% (581/581), done.
remote: Total 630 (delta 172), reused 0 (delta 0)
Receiving objects: 100% (630/630), 1.22 MiB | 0 bytes/s, done.
Resolving deltas: 100% (172/172), done.
Checking connectivity... done.
仮想マシンイメージには、クローンできるサンプルプロジェクトが付属しています。ここではHTTPS経由で、上記で作成したjohn
ユーザーでクローンしています。Gitはこの接続のために認証情報を要求しますが、認証情報キャッシュにより、その後のすべてのリクエストでこのステップをスキップできます。
Fusionの設定
Git Fusionをインストールしたら、設定を調整したいと思うでしょう。これは、お気に入りのPerforceクライアントを使用すれば実際にはかなり簡単です。Perforceサーバー上の//.git-fusion
ディレクトリをワークスペースにマップするだけです。ファイル構造は次のようになります
$ tree
.
├── objects
│ ├── repos
│ │ └── [...]
│ └── trees
│ └── [...]
│
├── p4gf_config
├── repos
│ └── Talkhouse
│ └── p4gf_config
└── users
└── p4gf_usermap
498 directories, 287 files
objects
ディレクトリはGit Fusionによって内部的に使用され、PerforceオブジェクトをGitに、またその逆にもマッピングします。この中にあるものに手を出す必要はありません。このディレクトリにはグローバルなp4gf_config
ファイルがあり、各リポジトリにも1つずつあります。これらはGit Fusionの動作を決定する設定ファイルです。ルートにあるファイルを見てみましょう
[repo-creation]
charset = utf8
[git-to-perforce]
change-owner = author
enable-git-branch-creation = yes
enable-swarm-reviews = yes
enable-git-merge-commits = yes
enable-git-submodules = yes
preflight-commit = none
ignore-author-permissions = no
read-permission-check = none
git-merge-avoidance-after-change-num = 12107
[perforce-to-git]
http-url = none
ssh-url = none
[@features]
imports = False
chunked-push = False
matrix2 = False
parallel-push = False
[authentication]
email-case-sensitivity = no
ここではこれらのフラグの意味については触れませんが、これはGitが設定に使用するのとよく似たINI形式のテキストファイルであることに注意してください。このファイルはグローバルオプションを指定し、それらはrepos/Talkhouse/p4gf_config
のようなリポジトリ固有の設定ファイルによって上書きされる可能性があります。このファイルを開くと、グローバルデフォルトとは異なる設定を持つ[@repo]
セクションが表示されます。また、次のようなセクションも表示されます
[Talkhouse-master]
git-branch-name = master
view = //depot/Talkhouse/main-dev/... ...
これはPerforceブランチとGitブランチ間のマッピングです。セクションの名前は、一意である限り好きなようにつけることができます。git-branch-name
を使用すると、Gitでは扱いにくいデポパスをより分かりやすい名前に変換できます。view
設定は、標準のビューマッピング構文を使用して、PerforceファイルがGitリポジトリにどのようにマップされるかを制御します。この例のように、複数のマッピングを指定できます
[multi-project-mapping]
git-branch-name = master
view = //depot/project1/main/... project1/...
//depot/project2/mainline/... project2/...
このようにして、通常のワークスペースマッピングにディレクトリ構造の変更が含まれる場合でも、Gitリポジトリでそれを再現できます。
最後に説明するファイルはusers/p4gf_usermap
です。これはPerforceユーザーをGitユーザーにマッピングするもので、場合によっては必要ないかもしれません。PerforceのチェンジセットからGitコミットに変換する際、Git Fusionのデフォルトの動作は、Perforceユーザーを検索し、そこに保存されているメールアドレスとフルネームをGitの作成者/コミッターフィールドに使用することです。逆方向の変換では、デフォルトはGitコミットの作成者フィールドに保存されているメールアドレスでPerforceユーザーを検索し、そのユーザーとしてチェンジセットを提出することです(権限が適用されます)。ほとんどの場合、この動作で問題ありませんが、次のマッピングファイルを検討してください
john john@example.com "John Doe"
john johnny@appleseed.net "John Doe"
bob employeeX@example.com "Anon X. Mouse"
joe employeeY@example.com "Anon Y. Mouse"
各行は<user> <email> "<full name>"
の形式で、単一のユーザーマッピングを作成します。最初の2行は、2つの異なるメールアドレスを同じPerforceユーザーアカウントにマッピングします。これは、複数の異なるメールアドレスでGitコミットを作成した(またはメールアドレスを変更した)が、それらを同じPerforceユーザーにマッピングしたい場合に便利です。PerforceチェンジセットからGitコミットを作成する際、Perforceユーザーに一致する最初の行がGitの作成者情報に使用されます。
最後の2行は、作成されるGitコミットからBobとJoeの実際の名前とメールアドレスを隠します。これは、内部プロジェクトをオープンソース化したいが、従業員ディレクトリを全世界に公開したくない場合に便利です。メールアドレスとフルネームは、すべてのGitコミットを単一の架空の作成者に帰属させたい場合を除き、一意である必要があることに注意してください。
ワークフロー
Perforce Git Fusionは、PerforceとGitのバージョン管理間の双方向ブリッジです。Git側から作業するとどのように感じるか見てみましょう。上記のように設定ファイルを使用して「Jam」プロジェクトをマッピングしたと仮定します。これは次のようにクローンできます
$ git clone https://10.0.1.254/Jam
Cloning into 'Jam'...
Username for 'https://10.0.1.254': john
Password for 'https://john@10.0.1.254':
remote: Counting objects: 2070, done.
remote: Compressing objects: 100% (1704/1704), done.
Receiving objects: 100% (2070/2070), 1.21 MiB | 0 bytes/s, done.
remote: Total 2070 (delta 1242), reused 0 (delta 0)
Resolving deltas: 100% (1242/1242), done.
Checking connectivity... done.
$ git branch -a
* master
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/origin/rel2.1
$ git log --oneline --decorate --graph --all
* 0a38c33 (origin/rel2.1) Create Jam 2.1 release branch.
| * d254865 (HEAD, origin/master, origin/HEAD, master) Upgrade to latest metrowerks on Beos -- the Intel one.
| * bd2f54a Put in fix for jam's NT handle leak.
| * c0f29e7 Fix URL in a jam doc
| * cc644ac Radstone's lynx port.
[...]
これを最初に行う際には、時間がかかる場合があります。Git FusionがPerforce履歴内の該当するすべてのチェンジセットをGitコミットに変換しています。これはサーバー上でローカルに行われるため比較的速いですが、大量の履歴がある場合はそれでも時間がかかることがあります。その後のフェッチは増分変換を行うため、Git本来の速度に近い感覚になるでしょう。
ご覧のとおり、私たちのリポジトリは他のGitリポジトリとまったく同じように見えます。3つのブランチがあり、Gitはorigin/master
を追跡するローカルのmaster
ブランチを便利に作成してくれました。少し作業をして、新しいコミットをいくつか作成しましょう
# ...
$ git log --oneline --decorate --graph --all
* cfd46ab (HEAD, master) Add documentation for new feature
* a730d77 Whitespace
* d254865 (origin/master, origin/HEAD) Upgrade to latest metrowerks on Beos -- the Intel one.
* bd2f54a Put in fix for jam's NT handle leak.
[...]
新しいコミットが2つあります。さて、他に誰か作業していたか確認しましょう
$ git fetch
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://10.0.1.254/Jam
d254865..6afeb15 master -> origin/master
$ git log --oneline --decorate --graph --all
* 6afeb15 (origin/master, origin/HEAD) Update copyright
| * cfd46ab (HEAD, master) Add documentation for new feature
| * a730d77 Whitespace
|/
* d254865 Upgrade to latest metrowerks on Beos -- the Intel one.
* bd2f54a Put in fix for jam's NT handle leak.
[...]
誰かが作業していたようです!このビューからはわかりませんが、6afeb15
コミットは実際にはPerforceクライアントを使用して作成されました。Gitの視点からは別のコミットに見えるだけで、それがまさにポイントです。Perforceサーバーがマージコミットをどのように処理するか見てみましょう
$ git merge origin/master
Auto-merging README
Merge made by the 'recursive' strategy.
README | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git push
Counting objects: 9, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (9/9), done.
Writing objects: 100% (9/9), 917 bytes | 0 bytes/s, done.
Total 9 (delta 6), reused 0 (delta 0)
remote: Perforce: 100% (3/3) Loading commit tree into memory...
remote: Perforce: 100% (5/5) Finding child commits...
remote: Perforce: Running git fast-export...
remote: Perforce: 100% (3/3) Checking commits...
remote: Processing will continue even if connection is closed.
remote: Perforce: 100% (3/3) Copying changelists...
remote: Perforce: Submitting new Git commit objects to Perforce: 4
To https://10.0.1.254/Jam
6afeb15..89cba2b master -> master
Gitはうまくいったと考えています。p4v
のリビジョングラフ機能を使って、Perforceの視点からREADME
ファイルの履歴を見てみましょう

このビューを初めて見る方は混乱するかもしれませんが、Git履歴のグラフィカルビューアと同じ概念を示しています。私たちはREADME
ファイルの履歴を見ているため、左上のディレクトリツリーには、そのファイルがさまざまなブランチにどのように現れるかだけが表示されます。右上には、ファイルの異なるリビジョンがどのように関連しているかを示す視覚的なグラフがあり、このグラフの全体像は右下にあります。ビューの残りの部分は、選択されたリビジョン(この場合は2
)の詳細ビューに充てられます。
注目すべき点の1つは、グラフがGitの履歴とまったく同じに見えることです。Perforceには1
と2
のコミットを保存するための名前付きブランチがなかったため、それを保持するために.git-fusion
ディレクトリ内に「匿名」ブランチを作成しました。これは、名前付きのPerforceブランチに対応しない名前付きのGitブランチでも同様に発生します(そして、後で設定ファイルを使用してそれらをPerforceブランチにマッピングできます)。
ほとんどのことは舞台裏で行われますが、最終的な結果として、チームの一人はGitを使用し、もう一人はPerforceを使用しているにもかかわらず、互いの選択について知ることはありません。
Git-Fusionのまとめ
Perforceサーバーへのアクセス権がある(または取得できる)場合、Git FusionはGitとPerforceを連携させる優れた方法です。多少の設定は必要ですが、学習曲線はそれほど急ではありません。この章でGitの全機能を使用する際の注意点が登場しない数少ないセクションの1つです。これは、Perforceがあなたが投げかけるすべてに満足するわけではない、つまりすでにプッシュされた履歴を書き換えようとするとGit Fusionはそれを拒否しますが、Git Fusionは非常にネイティブな感触を与えるよう努力しているという意味です。Gitサブモジュールも使用できます(Perforceユーザーには奇妙に見えるかもしれませんが)、ブランチをマージすることも可能です(これはPerforce側で統合として記録されます)。
サーバー管理者を説得してGit Fusionをセットアップできない場合でも、これらのツールを一緒に使用する方法はまだあります。
Git-p4
Git-p4はGitとPerforce間の双方向ブリッジです。これはGitリポジトリ内で完全に動作するため、Perforceサーバーへのアクセス(ユーザー認証情報は除く)は一切必要ありません。Git-p4はGit Fusionほど柔軟で完全なソリューションではありませんが、サーバー環境に侵入することなく、ほとんどのやりたいことを行うことができます。
注
|
git-p4を操作するには、 |
セットアップ
例として、上記で示したGit Fusion OVAからPerforceサーバーを実行しますが、Git FusionサーバーをバイパスしてPerforceのバージョン管理に直接アクセスします。
p4
コマンドラインクライアント(git-p4が依存するもの)を使用するには、いくつかの環境変数を設定する必要があります
$ export P4PORT=10.0.1.254:1666
$ export P4USER=john
始め方
Gitのあらゆることと同様に、最初のコマンドはクローンです
$ git p4 clone //depot/www/live www-shallow
Importing from //depot/www/live into www-shallow
Initialized empty Git repository in /private/tmp/www-shallow/.git/
Doing initial import of //depot/www/live/ from revision #head into refs/remotes/p4/master
これはGitの用語で言う「シャロー」クローンを作成します。最も新しいPerforceのリビジョンだけがGitにインポートされます。Perforceはすべてのリビジョンをすべてのユーザーに提供するようには設計されていないことを覚えておいてください。これはGitをPerforceクライアントとして使用するには十分ですが、他の目的には十分ではありません。
完了すると、完全に機能するGitリポジトリができます
$ cd myproject
$ git log --oneline --all --graph --decorate
* 70eaf78 (HEAD, p4/master, p4/HEAD, master) Initial import of //depot/www/live/ from the state at revision #head
Perforceサーバーに「p4」リモートがある点に注目してください。しかし、それ以外は標準的なクローンに見えます。実際には、それは少し誤解を招きます。実際にはそこにリモートはありません。
$ git remote -v
このリポジトリにはリモートがまったく存在しません。Git-p4はサーバーの状態を表すためにいくつかの参照を作成し、それらはgit log
からはリモート参照のように見えますが、Git自体によって管理されているわけではなく、それらにプッシュすることはできません。
ワークフロー
では、少し作業をしましょう。非常に重要な機能でいくつかの進捗があり、チームの残りのメンバーに見せる準備ができたと仮定します。
$ git log --oneline --all --graph --decorate
* 018467c (HEAD, master) Change page title
* c0fb617 Update link
* 70eaf78 (p4/master, p4/HEAD) Initial import of //depot/www/live/ from the state at revision #head
Perforceサーバーに提出する準備ができた新しいコミットが2つあります。今日、他に誰か作業していたか確認しましょう
$ git p4 sync
git p4 sync
Performing incremental import into refs/remotes/p4/master git branch
Depot paths: //depot/www/live/
Import destination: refs/remotes/p4/master
Importing revision 12142 (100%)
$ git log --oneline --all --graph --decorate
* 75cd059 (p4/master, p4/HEAD) Update copyright
| * 018467c (HEAD, master) Change page title
| * c0fb617 Update link
|/
* 70eaf78 Initial import of //depot/www/live/ from the state at revision #head
彼らも作業していたようで、master
とp4/master
は分岐しています。PerforceのブランチシステムはGitとはまったく異なり、マージコミットを提出しても意味がありません。Git-p4はコミットをリベースすることを推奨しており、そのためのショートカットも用意されています
$ git p4 rebase
Performing incremental import into refs/remotes/p4/master git branch
Depot paths: //depot/www/live/
No changes to import!
Rebasing the current branch onto remotes/p4/master
First, rewinding head to replay your work on top of it...
Applying: Update link
Applying: Change page title
index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
出力から察するかもしれませんが、git p4 rebase
はgit p4 sync
に続くgit rebase p4/master
のショートカットです。特に複数のブランチで作業している場合はもう少し賢いですが、これは良い近似値です。
これで私たちの履歴は再び線形になり、変更をPerforceに貢献する準備が整いました。git p4 submit
コマンドは、p4/master
とmaster
の間にある各Gitコミットに対して新しいPerforceリビジョンを作成しようとします。これを実行すると、お気に入りのエディタが起動し、ファイルの内容は次のようになります
# A Perforce Change Specification.
#
# Change: The change number. 'new' on a new changelist.
# Date: The date this specification was last modified.
# Client: The client on which the changelist was created. Read-only.
# User: The user who created the changelist.
# Status: Either 'pending' or 'submitted'. Read-only.
# Type: Either 'public' or 'restricted'. Default is 'public'.
# Description: Comments about the changelist. Required.
# Jobs: What opened jobs are to be closed by this changelist.
# You may delete jobs from this list. (New changelists only.)
# Files: What opened files from the default changelist are to be added
# to this changelist. You may delete files from this list.
# (New changelists only.)
Change: new
Client: john_bens-mbp_8487
User: john
Status: new
Description:
Update link
Files:
//depot/www/live/index.html # edit
######## git author ben@straub.cc does not match your p4 account.
######## Use option --preserve-user to modify authorship.
######## Variable git-p4.skipUserNameCheck hides this message.
######## everything below this line is just the diff #######
--- //depot/www/live/index.html 2014-08-31 18:26:05.000000000 0000
+++ /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/index.html 2014-08-31 18:26:05.000000000 0000
@@ -60,7 +60,7 @@
</td>
<td valign=top>
Source and documentation for
-<a href="http://www.perforce.com/jam/jam.html">
+<a href="jam.html">
Jam/MR</a>,
a software build tool.
</td>
これはp4 submit
を実行したときに表示される内容とほとんど同じですが、git-p4が親切にも追加してくれた末尾の部分を除きます。Git-p4は、コミットまたはチェンジセットの名前を提供する必要がある場合、GitとPerforceの設定を個別に尊重しようとしますが、場合によってはそれを上書きしたいと思うでしょう。たとえば、インポートするGitコミットがPerforceユーザーアカウントを持たない貢献者によって書かれた場合でも、結果として生成されるチェンジセットが彼らが書いたものであるかのように見せたい(そしてあなたが書いたものではないように見せたい)と考えるかもしれません。
Git-p4は、GitコミットからのメッセージをこのPerforceチェンジセットの内容として便利にインポートしてくれるので、あとは保存して終了するだけです(各コミットにつき1回)。結果のシェル出力は次のようになります
$ git p4 submit
Perforce checkout for depot path //depot/www/live/ located at /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/
Synchronizing p4 checkout...
... - file(s) up-to-date.
Applying dbac45b Update link
//depot/www/live/index.html#4 - opened for edit
Change 12143 created with 1 open file(s).
Submitting change 12143.
Locking 1 files ...
edit //depot/www/live/index.html#5
Change 12143 submitted.
Applying 905ec6a Change page title
//depot/www/live/index.html#5 - opened for edit
Change 12144 created with 1 open file(s).
Submitting change 12144.
Locking 1 files ...
edit //depot/www/live/index.html#6
Change 12144 submitted.
All commits applied!
Performing incremental import into refs/remotes/p4/master git branch
Depot paths: //depot/www/live/
Import destination: refs/remotes/p4/master
Importing revision 12144 (100%)
Rebasing the current branch onto remotes/p4/master
First, rewinding head to replay your work on top of it...
$ git log --oneline --all --graph --decorate
* 775a46f (HEAD, p4/master, p4/HEAD, master) Change page title
* 05f1ade Update link
* 75cd059 Update copyright
* 70eaf78 Initial import of //depot/www/live/ from the state at revision #head
結果は、まるでgit push
を行ったかのようなもので、実際に起こったことに最も近い類推です。
このプロセス中、すべてのGitコミットがPerforceチェンジセットに変換されることに注意してください。もしそれらを単一のチェンジセットにまとめるには、git p4 submit
を実行する前にインタラクティブなリベースで行うことができます。また、チェンジセットとして提出されたすべてのコミットのSHA-1ハッシュが変更されていることにも注意してください。これは、git-p4が変換する各コミットの末尾に1行追加するためです
$ git log -1
commit 775a46f630d8b46535fc9983cf3ebe6b9aa53145
Author: John Doe <john@example.com>
Date: Sun Aug 31 10:31:44 2014 -0800
Change page title
[git-p4: depot-paths = "//depot/www/live/": change = 12144]
マージコミットを提出しようとするとどうなるでしょうか?試してみましょう。私たちが陥った状況は次のとおりです
$ git log --oneline --all --graph --decorate
* 3be6fd8 (HEAD, master) Correct email address
* 1dcbf21 Merge remote-tracking branch 'p4/master'
|\
| * c4689fc (p4/master, p4/HEAD) Grammar fix
* | cbacd0a Table borders: yes please
* | b4959b6 Trademark
|/
* 775a46f Change page title
* 05f1ade Update link
* 75cd059 Update copyright
* 70eaf78 Initial import of //depot/www/live/ from the state at revision #head
GitとPerforceの履歴は775a46f
の後で分岐しています。Git側には2つのコミットがあり、その後Perforceのヘッドとのマージコミットがあり、さらに別のコミットがあります。これらをPerforce側で単一のチェンジセットの上に提出してみましょう。今提出しようとしたらどうなるか見てみましょう
$ git p4 submit -n
Perforce checkout for depot path //depot/www/live/ located at /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/
Would synchronize p4 checkout in /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/
Would apply
b4959b6 Trademark
cbacd0a Table borders: yes please
3be6fd8 Correct email address
-n
フラグは--dry-run
の短縮形で、実際にsubmitコマンドを実行した場合に何が起こるかを報告しようとします。この場合、Perforceサーバーにまだ存在しない3つの非マージコミットに対応する3つのPerforceチェンジセットが作成されるようです。それはまさに私たちが望むものでしょう。結果がどうなるか見てみましょう
$ git p4 submit
[…]
$ git log --oneline --all --graph --decorate
* dadbd89 (HEAD, p4/master, p4/HEAD, master) Correct email address
* 1b79a80 Table borders: yes please
* 0097235 Trademark
* c4689fc Grammar fix
* 775a46f Change page title
* 05f1ade Update link
* 75cd059 Update copyright
* 70eaf78 Initial import of //depot/www/live/ from the state at revision #head
私たちの履歴は再び線形になりました。提出する前にリベースしたかのように(実際にその通りです)。これは、Git側でブランチを作成し、作業し、破棄し、マージしても、履歴がPerforceと互換性がなくなる心配がないことを意味します。リベースできるのであれば、Perforceサーバーに貢献できます。
ブランチ
Perforceプロジェクトに複数のブランチがある場合でも、諦める必要はありません。git-p4はGitのように感じられる方法でそれを処理できます。あなたのPerforceデポがこのように配置されているとしましょう
//depot
└── project
├── main
└── dev
そして、次のようなビュー仕様を持つdev
ブランチがあるとしましょう
//depot/project/main/... //depot/project/dev/...
Git-p4はその状況を自動的に検出し、適切に対応できます
$ git p4 clone --detect-branches //depot/project@all
Importing from //depot/project@all into project
Initialized empty Git repository in /private/tmp/project/.git/
Importing revision 20 (50%)
Importing new branch project/dev
Resuming with change 20
Importing revision 22 (100%)
Updated branches: main dev
$ cd project; git log --oneline --all --graph --decorate
* eae77ae (HEAD, p4/master, p4/HEAD, master) main
| * 10d55fb (p4/project/dev) dev
| * a43cfae Populate //depot/project/main/... //depot/project/dev/....
|/
* 2b83451 Project init
デポパスの「@all」指定子に注目してください。これはgit-p4に、そのサブツリーの最新の変更セットだけでなく、それらのパスに触れたすべての変更セットをクローンするよう指示します。これはGitのクローン概念に近いですが、長い履歴を持つプロジェクトで作業している場合は、時間がかかる可能性があります。
--detect-branches
フラグは、git-p4にPerforceのブランチ仕様を使用してブランチをGit参照にマッピングするよう指示します。これらのマッピングがPerforceサーバーに存在しない場合(これはPerforceを使用する上で完全に有効な方法です)、git-p4にブランチマッピングが何であるかを伝えることができ、同じ結果が得られます
$ git init project
Initialized empty Git repository in /tmp/project/.git/
$ cd project
$ git config git-p4.branchList main:dev
$ git clone --detect-branches //depot/project@all .
git-p4.branchList
設定変数をmain:dev
に設定すると、git-p4は「main」と「dev」の両方がブランチであり、2番目のものが最初のブランチの子であることを認識します。
ここでgit checkout -b dev p4/project/dev
を実行してコミットすると、git p4 submit
を実行する際にgit-p4は正しいブランチを対象とするほど賢いです。残念ながら、git-p4はシャロークローンと複数のブランチを混在させることはできません。もし巨大なプロジェクトがあり、複数のブランチで作業したい場合は、提出したいブランチごとに一度git p4 clone
を実行する必要があります。
ブランチの作成や統合には、Perforceクライアントを使用する必要があります。Git-p4は既存のブランチへの同期と提出しかできず、一度に1つの線形チェンジセットしか処理できません。Gitで2つのブランチをマージして新しいチェンジセットを提出しようとすると、記録されるのは単なる多数のファイル変更のみとなり、統合に関与したブランチに関するメタデータは失われます。
GitとPerforceのまとめ
Git-p4はPerforceサーバーでGitワークフローを使用することを可能にし、それは非常にうまく機能します。しかし、Perforceがソースを管理しており、あなたはGitをローカルでの作業にのみ使用していることを覚えておくことが重要です。Gitコミットの共有には本当に注意してください。もし他の人が使用するリモートがある場合、Perforceサーバーにすでに提出されていないコミットはプッシュしないでください。
PerforceとGitをソース管理のクライアントとして自由に混在させたい場合で、サーバー管理者を説得してインストールできるのであれば、Git FusionはGitをPerforceサーバーの第一級バージョン管理クライアントにします。