Skip to content

Latest commit

 

History

History
980 lines (861 loc) · 58.1 KB

File metadata and controls

980 lines (861 loc) · 58.1 KB

Git と Perforce

Perforce は企業内では非常によく使われているバージョン管理システムです。 Perforce が登場したのは 1995 年で、この章で扱う中ではもっとも古いシステムです。 そしてその言葉通り、Perforce は当時の制約に合わせて設計されています。単一の中央サーバへの常時接続を仮定しており、またローカルディスクに保存しておけるバージョンは一つだけです。 確かに、Perforce の機能と制約は、ある特定の問題にはうまく適合します。しかし、実際には Git の方が上手くいくにも関わらず、 Perforce を使用しているというプロジェクトも数多くあります。

Perforce と Git を混在して使用したい場合、2通りの選択肢があります。 1つ目に取り上げるのは Perforce の開発元から出ている ``Git Fusion'' ブリッジです。これは、 Perforce ディポのサブツリーを読み書き可能な Git リポジトリとして公開させるものです。 2つ目はクライアントサイドのブリッジである git-p4 です。これは Git を Perforce のクライアントとして使用できるようにするもので、 Perforce サーバの設定を変更することなく使用できます。

Git Fusion

Perforce は Git Fusion という製品を提供しています( http://www.perforce.com/git-fusion から入手可能)。これは、サーバサイドで Perforce サーバと Git リポジトリとを同期させます。

セットアップ

本書の例では、 Git Fusion のもっとも簡単なインストール法として、仮想マシンをダウンロードし、 Perforce デーモンと Git Fusion をその上で実行する方法をとります。 仮想マシンイメージは http://www.perforce.com/downloads/Perforce/20-User から入手できます。ダウンロードが完了したら、お好みの仮想ソフトへインポートします(本書では VirtualBox を使用します)。

仮想マシンの初回起動時には、3つの Linux ユーザ( rootperforce 、および git )のパスワードを設定するよう要求されます。また、同じネットワーク上の他の仮想マシンとの区別のため、インスタンス名を決めるよう要求されます。 これらすべてが完了したら、次の画面が表示されるはずです。

Git Fusion 仮想マシンのブート画面
Figure 1. Git Fusion 仮想マシンのブート画面

ここで表示されるIPアドレスは、後で使用するので、メモしておいてください。 次に、 Perforce ユーザを作成します。 下部にある `Login'' オプションを選択肢、Enterキーを押下し(または仮想マシンへSSHで接続し)、 `root としてログインします。 続けて、次のコマンドでユーザを作成します。

$ p4 -p localhost:1666 -u super user -f john
$ p4 -p localhost:1666 -u john passwd
$ exit

1つめのコマンドは、ユーザのカスタマイズのため 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.

仮想マシンイメージには、クローンできるサンプルプロジェクトが同梱されています。 ここでは、上で作成したユーザ john を使用し、 HTTPS 経由でクローンしています。今回の接続時には認証情報を要求されますが、以降のリクエストでは 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 ディレクトリは Perforce のオブジェクトを Git へ対応付ける(逆も同様)ために Git Fusion が内部的に使用しているもので、この内容に触れる必要はありません。 このディレクトリにはグローバルな p4gf_config 設定ファイルがあります。また、このファイルは各リポジトリにも一つずつあります – これらは、 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"

各行のフォーマットは <ユーザ名> <メールアドレス> "<氏名>" となっています。一行ごとに一つ、ユーザの対応づけを定義しています。 最初の2行は、2つの異なるメールアドレスを同一の Perforce ユーザアカウントへ対応づけています。 これは、 Git のコミットを複数のメールアドレスで作成していた(または、メールアドレスを変更した)際に、それらを同じ Perforce へ対応づけたい場合に便利です。 Perforce のチェンジセットから Git のコミットを作成する際には、 Perforce のユーザとマッチした最初の行が Git の作成者情報として使用されます。

最後の2行は、Git のコミットから Bob と Joe の氏名とメールアドレスが分からないようにします。 これは、社内のプロジェクトをオープンソース化したいが、社員名簿を全世界へ晒したくはない、というときに役立ちます。 注意すべき点として、すべての Git コミットが実際には存在しない1人のユーザによるものである、としたい場合を除き、メールアドレスと氏名は一意になるよう設定してください。

ワークフロー

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://ben@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 ブランチまで作成してくれています。 ちょっと作業をして、新しいコミットを2つほど作成してみましょう。

# ...
$ 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 のリビジョングラフ機能を使用して、 README ファイルの歴史を Perforce の視点から見てみましょう。

Git でのプッシュの結果作成される Perforce のリビジョングラフ
Figure 2. Git でのプッシュの結果作成される Perforce のリビジョングラフ

この画面を見たことがないと、混乱するかもしれませんが、 Git の歴史をグラフィカルに表示するビューアと同じ概念を示しています。 ここでは README ファイルの歴史を見ています。そのため、左上のディレクトリツリーでは、様々なブランチなかからそのファイルだけを取り上げて表示しています。 右上には、そのファイルの様々なリビジョンがどのように関連しているか、ビジュアライズされたグラフが表示されます。このグラフの全体像は右下にあります。 ビューの残りの部分では、選択したリビジョン(この場合は 2 )の詳細を表示しています。

ひとつ注目してもらいたいのは、このグラフが Git の歴史のグラフとそっくりだということです。 Perforce にはコミット 1 および 2 を格納する名前つきのブランチがありません。代わりに `anonymous'' ブランチを `.git-fusion ディレクトリに作成し、そこに保管しています。 同様のことは、名前つきの Git のブランチに、対応する名前つきの Perforce のブランチがない場合にも起こります(設定ファイルを使えば、後でそのようなブランチを Perforce のブランチへ対応づけることも可能です)。

これのほとんどは舞台裏で行われますが、最終的には、ひとつのチームの中で、ある人は Git を使用し、またある人は Perforce を使用するということができ、その双方とも他の人が何を使用しているのかを知ることはない、という結果になりました。

Git-Fusion のまとめ

Perforce サーバへのアクセス権がある(または取得できる)なら、 Git Fusion は Git と Perforce が互いにやりとりできるようにする素晴らしい方法です。 多少の設定は必要ですが、学習曲線は急ではありません。 本節は、この章において、 Git の全機能を使用することに関して注意事項のない、数少ない節の一つです。 Perforce は指定した処理すべてを喜んでこなす、とは言いません – すでにプッシュ済みの歴史を書き換えようとしたら、 Git Fusion はそれをリジェクトします – ですが、 Git Fusion は Git そのままの感じになるよう非常に苦心しています。 また、Git のサブモジュールを使用したり(Perforce のユーザには変な風にみえますが)、ブランチのマージをしたり(Perforce 側では統合として記録されます)することも可能です。

Git Fusion をセットアップするようサーバの管理者を説得できなかったとしても、Git と Perforce を一緒に使用する方法は他にもあります。

Git-p4

git-p4 は、 Git と Perforce の間の双方向ブリッジです。 git-p4 は Git リポジトリの中だけで動作するので、 Perforce サーバにおけるいかなるアクセス権も必要としません(もちろん、ユーザ権限は必要ですが)。 git-p4 には Git Fusion ほどの柔軟性や完全性はありません。ですが、やりたいであろうことの大半を、サーバ環境に対して侵襲的になることなく実施できます。

Note

git-p4 で作業を行う場合、 p4 ツールに PATH が通っている必要があります。 これを書いている時点では、 p4http://www.perforce.com/downloads/Perforce/20-User から無料で入手できます。

セットアップ

例のため、前に見てきたとおり Perforce サーバを Git Fusion OVA で実行しますが、ここでは Git Fusion サーバをバイパスして、 Perforce のバージョン管理機能を直接使用します。

p4 コマンドラインクライアント( git-p4 がこれに依存している)を使用するには、環境変数を2つ設定する必要があります。

$ 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

すでに2つのコミットを作成しており、Perforce サーバへ送信する準備もできています。 今日、他の誰かが作業をしていなかったか確認してみましょう。

$ 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

誰かが作業をしていたようです。また、 masterp4/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 rebasegit p4 sync の後に git rebase p4/master を実行するショートカットです。 実際にはもう少し賢いのですが(特に複数のブランチで作業をしている場合には)、これはよい近似と言えます。

これで歴史がリニアになり、変更を Perforce へ提供できる状態になりました。 git p4 submit を実行すると、 Git の p4/master から master の間の各コミットに対して 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>

これは、 git-p4 が気を利かせて末尾に追加している内容を除けば、 p4 submit を実行した場合とほぼ同じ内容です。 git-p4 は、コミットやチェンジセットに対して氏名を指定する必要がある場合、 Git と Perforce で設定をそれぞれ個別に行えるようにしていますが、その設定を上書きしたい場合もあります。 例えば、 Git のコミットをインポートしていて、そのコミットの作成者が Perforce のユーザアカウントを持っていない場合を考えます。この場合、最終的にできるチェンジセットは(あなたではなく)そのコミットの作成者が書いたように見えるようにしたいだろうと思います。

git-p4 は気が利いていて、Git のコミットからメッセージをインポートして、この Perforce のチェンジセットの内容にセットしてくれます。そのため、ここではファイルの保存とエディタの終了を2回(コミット1つにつき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 のチェンジセットに変換されます。複数のコミットを1つのチェンジセットへスカッシュしたい場合は、 git p4 submit の前に対話的リベースを行ってください。 また、チェンジセットとして送信された全コミットの SHA-1 ハッシュが変更される点にも注意してください。これは、 git-p4 が変換した各コミットの末尾に行を追加するためです。

$ 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 側の HEAD とのマージコミット、さらにその次にまた別のコミットがあります。 ここでは、これらすべてのコミットを 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 の省略形で、送信コマンドを本当に実行したら何が起こるかを表示してくれます。 この場合、 Perforce サーバに存在しない3つの非マージコミットに対応するように、 Perforce のチェンジセットを3つ作成するように見えます。 これは、望む結果そのもののように見えますので、結果どうなったか見てみましょう。

$ 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 サーバになかった場合(これは 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.branchListmain:dev を設定すると、 main'' と dev'' がいずれもブランチであること、2つめのブランチは1つめのブランチの子であることを git-p4 へ示します。

ここで git checkout -b dev p4/project/dev を実行してからコミットを作成した場合でも、 git-p4 は充分に賢いので、 git p4 submit を実行した際には正しいブランチを対象にしてくれます。 なお、残念なことに、 git-p4 ではシャロークローンと複数ブランチを混ぜて使うことができません。巨大なプロジェクトにおいて複数のブランチで作業したい場合、ブランチごとに git p4 clone を実行する必要があります。

また、ブランチの作成や統合には、 Perforce クライアントを使用する必要があります。 git-p4 にできるのは既存のブランチに対する同期と送信だけで、それも対象にできるのはリニアなチェンジセットを一度にひとつだけです。 Git で2つのブランチをマージして新しいチェンジセットを送信しても、一塊りのファイルの変更として記録されるだけです。マージの対象となったブランチはどれかといったメタデータは失われてしまいます。

Git と Perforce のまとめ

git-p4 は、 Git のワークフローを Perforce サーバ上で使用できるようにします。また、それを非常にうまいやり方で可能にします。 ですが、大元を管理しているのはあくまで Perforce であり、 Git はローカルでの作業にのみ使用しているということは忘れないでください。 Git のコミットの共有については特に気をつけてください。他のメンバーが使用しているリモートがある場合、 Perforce サーバに送信していないコミットをプッシュしないよう気をつけてください。

ソース管理のクライアントに Perforce と Git を自由に混ぜて使いたい場合、さらにサーバの管理者を説得してインストールの許可を貰える場合、Git Fusion は Git を Perforce サーバ用の第一級のバージョン管理クライアントにしてくれます。