Git は Windows を利用する開発者の間でもよく使われるようになってきています。 Windows 上でコードを書いているのなら、 Microsoft の Team Foundation Server (TFS) を使用することもあるでしょう。 TFS はコラボレーションスイートで、不具合および作業成果物に対するトラッキング、 Scrum やその他の開発プロセスのサポート、コードレビュー、そしてバージョン管理といった機能が含まれています。 ここがちょっとややこしいところなのですが、 TFS 自体はサーバで、ソースコード管理には、 Git や TFS 専用のバージョン管理システム( TFVC (Team Foundation Version Control) とも呼ばれる)をサポートしています。 TFS の Git サポートは幾分新しい機能(バージョン2013から搭載)なので、それ以前からあったツールはどれも、実際にはほぼ TFVC だけを使用している場合であっても、バージョン管理部分のことを ``TFS'' と呼んでいます。
所属しているチームは TFVC を使用しているけれど、あなた自身はバージョン管理のクライアントに Git を使用したいという場合には、そのためのプロジェクトがあります。
実際には、ツールは2つあります。 git-tf と git-tfs です。
git-tfs ( https://github.com/git-tfs/git-tfs から入手できます)は .NET プロジェクトで、(これを書いている時点では)Windows でのみ動作します。 git-tfs は、Git リポジトリに対する操作に libgit2 の .NET バインディングを使用しています。libgit2 はライブラリ指向の Git の実装で、処理性能が高く、また Git の内部に対して柔軟な操作が行えるようになっています。 libgit2 は Git の機能を網羅的に実装してはいないため、その差を埋めるために、 git-tfs の一部の操作では実際にはコマンドライン版の Git クライアントが呼び出されています。そのため、Git リポジトリに対する操作に関して、git-tfs の設計に起因した制約は特にありません。 git-tfs は、 TFS サーバの操作に Visual Studio のアセンブリを使用しているため、TFVC 向け機能は非常に成熟しています。 また、これは Visual Studio のアセンブリにアクセスできる必要があるということでもあります。そのため、比較的新しいバージョンの Visual Studio (Visual Studio 2010以降の任意のエディション。バージョン2012以降の Visual Studio Express でもよい)か、Visual Studio SDK のインストールが必要です。
git-tf (ホームページは https://gittf.codeplex.com )は Java プロジェクトで、Java実行環境のあるあらゆるコンピュータで実行できます。 git-tf は Git リポジトリに対する操作に JGit (JVM 用の Git の実装)を使用しているため、 Git の機能という観点においては事実上制約はありません。 しかし、 TFVC に対するサポートは git-tfs と比較すると限定的です – 例えば、ブランチをサポートしていません。
どちらのツールにも長所と短所があり、また一方よりもう一方が向いている状況というのはいくらでもあります。 本書では、2つのツールの基本的な使用法について取り上げます。
Note
|
以降の手順に従って操作を行うには、 TFVC ベースのリポジトリへのアクセス権が必要です。 そのようなリポジトリは Git や Subversion のリポジトリほど世の中にありふれたものではないので、自前で作成する必要があるかもしれません。 この場合、 Codeplex (https://www.codeplex.com) や Visual Studio Online (http://www.visualstudio.com) を利用するのがよいでしょう。 |
最初に行うことは、あらゆる Git プロジェクトと同様、クローンです。
git-tf
は次のような感じです。
$ git tf clone https://tfs.codeplex.com:443/tfs/TFS13 $/myproject/Main project_git
最初の引数は TFVC コレクションの URL で、2番目の引数は $/プロジェクト/ブランチ
の形式になっており、3番目の引数はローカルに作成する Git リポジトリのパスです(3番目の引数はオプショナルです)。
git-tf は一度にひとつのブランチしか扱えません。別の TFVC ブランチへチェックインしたい場合は、対象のブランチから新しくクローンを作成する必要があります。
次のコマンドで、フル機能の Git リポジトリが作成できます。
$ cd project_git
$ git log --all --oneline --decorate
512e75a (HEAD, tag: TFS_C35190, origin_tfs/tfs, master) Checkin message
これは シャロー クローンと言われるもので、最新のチェンジセットだけがダウンロードされます。 TFVC は、各クライアントが歴史の完全なコピーを持つようには設計されていません。そのため git-tf は、幾分高速な、最新のバージョンだけを取得する方法をデフォルトとしています。
時間があるなら、プロジェクトの歴史全体をクローンしてみるといいでしょう。 --deep
オプションを使用します。
$ git tf clone https://tfs.codeplex.com:443/tfs/TFS13 $/myproject/Main \
project_git --deep
Username: domain\user
Password:
Connecting to TFS...
Cloning $/myproject into /tmp/project_git: 100%, done.
Cloned 4 changesets. Cloned last changeset 35190 as d44b17a
$ cd project_git
$ git log --all --oneline --decorate
d44b17a (HEAD, tag: TFS_C35190, origin_tfs/tfs, master) Goodbye
126aa7b (tag: TFS_C35189)
8f77431 (tag: TFS_C35178) FIRST
0745a25 (tag: TFS_C35177) Created team project folder $/tfvctest via the \
Team Project Creation Wizard
TFS_C35189
のような名前のタグに注目してください。これは、Git のどのコミットが、 TFVC のどのチェンジセットに対応しているかを分かりやすくするための機能です。
タグで表現するというのは上手い方法です。簡単な log コマンドだけで、どのコミットが TFVC 中のどのスナップショットに対応しているか確認できます。
なお、このタグ付けは必須ではありません(実際、 git config git-tf.tag false
で無効にできます) – git-tf では、コミットとチェンジセットとのマッピングは .git/git-tf
に保存されています。
git-tfs のクローン処理は、 git-tf とは少し異なります。 見てみましょう。
PS> git tfs clone --with-branches \
https://username.visualstudio.com/DefaultCollection \
$/project/Trunk project_git
Initialized empty Git repository in C:/Users/ben/project_git/.git/
C15 = b75da1aba1ffb359d00e85c52acb261e4586b0c9
C16 = c403405f4989d73a2c3c119e79021cb2104ce44a
Tfs branches found:
- $/tfvc-test/featureA
The name of the local branch will be : featureA
C17 = d202b53f67bde32171d5078968c644e562f1c439
C18 = 44cd729d8df868a8be20438fdeeefb961958b674
--with-branches
フラグに注意してください。
git-tfs では TFVC のブランチを Git のブランチへマッピングできます。ここで --with-branches
フラグは、 git-tfs に対し、すべての TFVC ブランチについて、対応するブランチをローカルの Git に作成するよう指示しています。
TFS 上で一度でもブランチの作成やマージを行っている場合、このオプションを指定することを強くお勧めします。ただし、このオプションは TFS 2010 より古いバージョンのサーバでは動作しません – それ以前のリリースでは ``ブランチ'' はただのフォルダだったためです。 git-tfs は単なるフォルダからはそのような指示は行えません。
結果の Git リポジトリを見てみましょう。
PS> git log --oneline --graph --decorate --all
* 44cd729 (tfs/featureA, featureA) Goodbye
* d202b53 Branched from $/tfvc-test/Trunk
* c403405 (HEAD, tfs/default, master) Hello
* b75da1a New project
PS> git log -1
commit c403405f4989d73a2c3c119e79021cb2104ce44a
Author: Ben Straub <ben@straub.cc>
Date: Fri Aug 1 03:41:59 2014 +0000
Hello
git-tfs-id: [https://username.visualstudio.com/DefaultCollection]$/myproject/Trunk;C16
2つのローカルブランチ master
と featureA
があり、それぞれ最初のクローンの開始位置( TFVC の Trunk
)と、子のブランチ( TFVC の featureA
)を表しています。
また `リモート'' `tfs
にも default
と featureA
の2つの参照があり、これは TFVC のブランチを表現しています。
git-tfs はクローン元のブランチを tfs/default
へマッピングし、それ以外のブランチにはそれぞれのブランチ名を付与します。
もうひとつ注意すべき点として、コミットメッセージにある git-tfs-id:
という行があります。
タグとは異なり、このマーカーは git-tfs が TFVC のチェンジセットを Git のコミットへ対応づけるのに使用しています。
これは、TFVC にプッシュする前と後とで Git のコミットの SHA-1 ハッシュが異なるということを暗黙的に意味しています。
Note
|
どちらのツールを使用するにせよ、問題を避けるため、次の2つの Git の設定値をセットする必要があります。 $ git config set --local core.ignorecase=true
$ git config set --local core.autocrlf=false |
当たり前ですが、次はプロジェクトに対して作業を行いたいことと思います。 TFVC および TFS には、ワークフローをややこしくする機能がいくつかあります。
-
TFVC 上に現れないフィーチャーブランチがあると、ややこしさが増します。 これには、TFVC と Git とでブランチを表現する方法が まったく 異なることが関係しています。
-
TFVC では、ユーザがサーバからファイルを ``チェックアウト'' して、他の誰も編集できないようにロックを掛けられることを認識しておいてください。 ローカルリポジトリ上でファイルを編集する妨げには当然なりませんが、変更を TFVC サーバへプッシュする段になって邪魔になるかもしれません。
-
TFS には
ゲート'' チェックインという概念があります。これは、チェックインが許可されるには、 TFS のビルドとテストのサイクルが正常に終了する必要がある、というものです。 これは TFVC の
シェルブ'' 機能を使用していますが、これについてはここでは深入りしません。 手作業でなら、git-tf でもこの方式をまねることはできます。 git-tfs はこれを考慮したcheckintool
コマンドを提供しています。
話を簡潔にするため、ここで取り上げるのは、これらの問題を避けたり起こらないようにした、ハッピーな手順です。
ここでは、いくつか作業を終えて、 master
に Git のコミットを2つ作成し、作業の成果を TFVC サーバで共有する準備ができているものとします。
Git リポジトリはこんな内容です。
$ git log --oneline --graph --decorate --all
* 4178a82 (HEAD, master) update code
* 9df2ae3 update readme
* d44b17a (tag: TFS_C35190, origin_tfs/tfs) Goodbye
* 126aa7b (tag: TFS_C35189)
* 8f77431 (tag: TFS_C35178) FIRST
* 0745a25 (tag: TFS_C35177) Created team project folder $/tfvctest via the \
Team Project Creation Wizard
4178a82
のコミットでスナップショットを取って、 TFVC サーバへプッシュしたいものとします。
大事なことから先にとりかかりましょう。最後にリポジトリへ接続した後に、チームの他のメンバーが何か作業をしていなかったか見てみます。
$ git tf fetch
Username: domain\user
Password:
Connecting to TFS...
Fetching $/myproject at latest changeset: 100%, done.
Downloaded changeset 35320 as commit 8ef06a8. Updated FETCH_HEAD.
$ git log --oneline --graph --decorate --all
* 8ef06a8 (tag: TFS_C35320, origin_tfs/tfs) just some text
| * 4178a82 (HEAD, master) update code
| * 9df2ae3 update readme
|/
* d44b17a (tag: TFS_C35190) Goodbye
* 126aa7b (tag: TFS_C35189)
* 8f77431 (tag: TFS_C35178) FIRST
* 0745a25 (tag: TFS_C35177) Created team project folder $/tfvctest via the \
Team Project Creation Wizard
やはり、他の誰かが作業をしているようです。歴史が分岐しています。 これは Git が得意とするところですが、進め方は2種類あります。
-
Git ユーザなら、マージコミットを行うのが自然に感じられるでしょう(
git pull
が行うのがマージコミットなので)。 git-tf では単にgit tf pull
とすればマージコミットが行えます。 ですが、ここで注意が必要なのは、 TFVC はこれを自然とは考えないということです。マージコミットをプッシュしたら、歴史は Git 側と TFVC 側とで異なる見た目になりだし、ややこしいことになります。 一方、すべての変更をひとつのチェンジセットとして送信しようとしているのであれば、おそらくそれがもっとも簡単な選択肢です。 -
リベースを行うと、歴史がリニアになります。つまり、 Git のコミットひとつひとつを TFVC のチェンジセットへ変換する選択肢がとれるということです。 これが、以降の選択肢をもっとも広く取れる方法なので、この方法をお勧めします。git-tf でも、
git tf pull --rebase
で簡単に行えるようになっています。
どの方法をとるかはあなた次第です。 この例では、リベースする方法をとったとします。
$ git rebase FETCH_HEAD
First, rewinding head to replay your work on top of it...
Applying: update readme
Applying: update code
$ git log --oneline --graph --decorate --all
* 5a0e25e (HEAD, master) update code
* 6eb3eb5 update readme
* 8ef06a8 (tag: TFS_C35320, origin_tfs/tfs) just some text
* d44b17a (tag: TFS_C35190) Goodbye
* 126aa7b (tag: TFS_C35189)
* 8f77431 (tag: TFS_C35178) FIRST
* 0745a25 (tag: TFS_C35177) Created team project folder $/tfvctest via the \
Team Project Creation Wizard
さて、これで TFVC サーバへチェックインする準備ができました。
git-tf では、直近のチェンジセット以降のすべての変更を単一のチェンジセットにまとめる( --shallow
、こっちがデフォルト)か、Git のコミットそれぞれに対して新しくチェンジセットを作成する( --deep
)かを選択できます。
この例では、単一のチェンジセットにまとめる方法をとったとします。
$ git tf checkin -m 'Updating readme and code'
Username: domain\user
Password:
Connecting to TFS...
Checking in to $/myproject: 100%, done.
Checked commit 5a0e25e in as changeset 35348
$ git log --oneline --graph --decorate --all
* 5a0e25e (HEAD, tag: TFS_C35348, origin_tfs/tfs, master) update code
* 6eb3eb5 update readme
* 8ef06a8 (tag: TFS_C35320) just some text
* d44b17a (tag: TFS_C35190) Goodbye
* 126aa7b (tag: TFS_C35189)
* 8f77431 (tag: TFS_C35178) FIRST
* 0745a25 (tag: TFS_C35177) Created team project folder $/tfvctest via the \
Team Project Creation Wizard
新しくタグ TFS_C35348
ができています。これは、 TFVC がコミット 5a0e25e
とまったく同じスナップショットを格納していることを意味します。
ここが重要なのですが、Git の各コミットが、すべて TFVC 側と対応づいている必要はありません。例えば、コミット 6eb3eb5
は、 TFVC サーバには存在しません。
以上が主なワークフローです。 他にも、考慮すべき点として気をつけるべきものが2つほどあります。
-
ブランチはできません。 git-tf にできるのは、 TFVC のブランチから、 Git のリポジトリを作ることだけで、それも一度にひとつずつしか作れません。
-
共同作業の際は、 TFVC と Git のいずれかだけを使用し、両方は使用しないでください。 ひとつの TFVC リポジトリから、 git-tf で複数のクローンを作成した場合、各コミットの SHA-1 ハッシュそれぞれ異なります。コミットが作成されます。これは終わることのない頭痛の種になります。
-
チームのワークフローに Git との協調作業が含まれており、定期的に TFVC との同期を取る場合、 TFVC へ接続する Git リポジトリはひとつだけにしてください。
git-tfs を使用した場合と同じシナリオを見ていきましょう。
Git リポジトリには、 master
ブランチに対して行った新しいコミットが入っています。
PS> git log --oneline --graph --all --decorate
* c3bd3ae (HEAD, master) update code
* d85e5a2 update readme
| * 44cd729 (tfs/featureA, featureA) Goodbye
| * d202b53 Branched from $/tfvc-test/Trunk
|/
* c403405 (tfs/default) Hello
* b75da1a New project
さて、我々が作業している間に、他の誰かが作業をしていなかったか見てみましょう。
PS> git tfs fetch
C19 = aea74a0313de0a391940c999e51c5c15c381d91d
PS> git log --all --oneline --graph --decorate
* aea74a0 (tfs/default) update documentation
| * c3bd3ae (HEAD, master) update code
| * d85e5a2 update readme
|/
| * 44cd729 (tfs/featureA, featureA) Goodbye
| * d202b53 Branched from $/tfvc-test/Trunk
|/
* c403405 Hello
* b75da1a New project
同僚が新しく TFVC のチェンジセットを追加しており、それが新しいコミット aea74a0
として表示されている他に、リモートブランチ tfs/default
が移動されていることがわかりました。
git-tf と同様に、この分岐した歴史を処理する基本的な方法は2つあります。
-
歴史をリニアに保つためにリベースを行う。
-
実際に起こったことを残しておくためマージを行う。
この例では、Git の各コミットが TFVC のチェンジセットになる ``ディープ'' なチェックインを行おうとしているので、リベースをします。
PS> git rebase tfs/default
First, rewinding head to replay your work on top of it...
Applying: update readme
Applying: update code
PS> git log --all --oneline --graph --decorate
* 10a75ac (HEAD, master) update code
* 5cec4ab update readme
* aea74a0 (tfs/default) update documentation
| * 44cd729 (tfs/featureA, featureA) Goodbye
| * d202b53 Branched from $/tfvc-test/Trunk
|/
* c403405 Hello
* b75da1a New project
これで、コードを TFVC サーバへチェックインして、 作業を完了する準備ができました。
ここでは rcheckin
コマンドを使用し、HEAD から始めて最初の tfs
リモートブランチが見つかるまでの Git の各コミットに対して、 TFVC のチェンジセットを作成します( checkin
コマンドでは、 Git のコミットをスカッシュするのと同様に、チェンジセットをひとつだけ作成します)。
PS> git tfs rcheckin
Working with tfs remote: default
Fetching changes from TFS to minimize possibility of late conflict...
Starting checkin of 5cec4ab4 'update readme'
add README.md
C20 = 71a5ddce274c19f8fdc322b4f165d93d89121017
Done with 5cec4ab4b213c354341f66c80cd650ab98dcf1ed, rebasing tail onto new TFS-commit...
Rebase done successfully.
Starting checkin of b1bf0f99 'update code'
edit .git\tfs\default\workspace\ConsoleApplication1/ConsoleApplication1/Program.cs
C21 = ff04e7c35dfbe6a8f94e782bf5e0031cee8d103b
Done with b1bf0f9977b2d48bad611ed4a03d3738df05ea5d, rebasing tail onto new TFS-commit...
Rebase done successfully.
No more to rcheckin.
PS> git log --all --oneline --graph --decorate
* ff04e7c (HEAD, tfs/default, master) update code
* 71a5ddc update readme
* aea74a0 update documentation
| * 44cd729 (tfs/featureA, featureA) Goodbye
| * d202b53 Branched from $/tfvc-test/Trunk
|/
* c403405 Hello
* b75da1a New project
TFVC サーバへのチェックインが成功した後に行われている処理に注目してください。 git-tfs は直前の処理結果に対して、残りの作業結果をリベースしています。
これは、コミットメッセージの末尾に git-tfs-id
フィールドを追記しており、SHA-1 ハッシュが変化するためです。
これは仕様通りの動作であり、何も心配することはありません。ですが、そのような変更がなされていることは(特に、 Git のコミットを他の人と共有している場合は)認識しておいてください。
TFS には、ワークアイテム、レビュー依頼、ゲートチェックインなど、バージョン管理システムと統合されている機能が数多くあります。 これらの機能をコマンドラインツールだけで使うのは大変ですが、幸いなことに、 git-tfs ではグラフィカルなチェックインツールを簡単に起動できるようになっています。
PS> git tfs checkintool
PS> git tfs ct
だいたいこんな感じで表示されます。
TFS ユーザは見慣れていると思いますが、これは Visual Studio から表示されるものと同じダイアログです。
git-tfs では TFVC のブランチを Git のリポジトリから管理することもできます。 例として、ひとつ作成してみましょう。
PS> git tfs branch $/tfvc-test/featureBee
The name of the local branch will be : featureBee
C26 = 1d54865c397608c004a2cadce7296f5edc22a7e5
PS> git log --oneline --graph --decorate --all
* 1d54865 (tfs/featureBee) Creation branch $/myproject/featureBee
* ff04e7c (HEAD, tfs/default, master) update code
* 71a5ddc update readme
* aea74a0 update documentation
| * 44cd729 (tfs/featureA, featureA) Goodbye
| * d202b53 Branched from $/tfvc-test/Trunk
|/
* c403405 Hello
* b75da1a New project
TFVC でブランチを作成するということは、そのブランチが存在する場所にチェンジセットを追加するということなので、結果としてそれは Git のコミットへ反映されます。
git-tfs はリモートブランチ tfs/featureBee
の 作成は しましたが、 HEAD
は相変わらず master
を指していることに注意してください。
新しく作成したブランチ上で作業をしたい場合は、コミット 1d54865
から新しいコミットを作成することになりますが、この場合は恐らくそのコミットから新しくトピックブランチを作成することになるでしょう。
git-tf と git-tfs は、いずれも TFVC サーバに接続するための優れたツールです。 これらのツールにより、チーム全体を Git へ移行することなしに、ローカルで Git のパワーを享受でき、中央の TFVC サーバを定期的に巡回しなくて済み、開発者としての生活をより楽にすることができます。 Windows 上で作業をしているのなら(チームが TFS を使用しているなら多分そうだと思いますが)、機能がより網羅的な git-tfs を使用したいことと思います。また別のプラットフォーム上で作業をしているのなら、より機能の限られている git-tf を使用することになると思います。 この章で取り上げているほとんどのツールと同様、バージョン管理システムのうちひとつだけを正式なものとして、他は従属的な使い方にしておくべきです – Git か TFVC の両方ではなく、いずれか片方を共同作業の中心に置くべきです。