Skip to content

Latest commit

 

History

History
558 lines (494 loc) · 27.3 KB

File metadata and controls

558 lines (494 loc) · 27.3 KB

Git と Mercurial

DVCSの世界にあるのはGitだけではありません。 事実、Git以外にも様々なシステムが存在し、分散バージョン管理を正しく行う方法について、それぞれが独自の見方を持っています。 Gitを除くと、もっとも広く使われているのは Mercurial です。Git と Mercurialは多くの点で似通っています。

良いニュースとして、 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 にはもう一つ依存先があります。 Python の mercurial ライブラリです。 Python をインストール済みなら、これは次のようにシンプルなコマンドで行えます。

$ pip install mercurial

(Python をインストールしていないなら、まず https://www.python.org/ からPython を入手してください。)

最後に必要なのは Mercurial のクライアントです。 インストール済みでないなら、 https://mercurial-scm.org/ から入手してインストールしてください。

これで準備が整いました。 必要なのはプッシュが可能な Mercurial リポジトリだけです。 幸いなことに、 Mercurial リポジトリならどれでもこの操作が可能です。そのため、 Mercurial の使い方を学ぶときにみんなが使う "hello world" リポジトリを使用することにします。

$ hg clone http://selenic.com/repo/hello /tmp/hello
使いはじめる

これで、都合のいい ``サーバサイド'' のリポジトリができたので、以降では典型的なワークフローを見ていきます。 これから見ていくように、 Git と Mercurial はよく似ているため、食い違う箇所はそう多くありません。

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

標準的な git clone コマンドを使用して Mercurial リポジトリを操作しているのが分かると思います。 これは git-remote-hg が非常に低いレベルで動作しているためです。 git-remote-hg は Git が HTTP/S プロトコルを実装しているのと同じようなメカニズム(リモートヘルパー)を使用しています。 Git と Mercurial は両方とも、すべてのクライアントがリポジトリの歴史の完全なコピーを持つように設計されています。そのためこのコマンドは、プロジェクトのすべての歴史を含む完全なクローンを作成します。また、この処理は非常に高速に行われます。

git logコマンドは2つのコミットを表示しています。最新のコミットは大量の参照から指されています。 実は、これらの中のいくつかは、実際には存在しません。 .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( `master が指しているコミットを表している)を含むGitの参照ファイルです。 そのため、 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 はツリー中の要素のモード、タイプ、オブジェクトハッシュ、およびファイル名を出力します。 ツリー中の要素の一つについて掘り下げていくと、その実体は ac9117f'' ( master が指しているコミットの SHA-1 ハッシュ)という名前の blob で、内容は 0a04b98'' ( default ブランチの先端の Mercurial チェンジセットのID)であることが分かります。

よいニュースとして、これらすべてのことについて、我々が気にする必要はほとんどありません。 典型的なワークフローは、 Git でリモートに対して作業をする場合と大差ありません。

以降の話をする前に、もう一つ注意しておかなければならないことがあります。 ignoreファイルです。 Mercurial と Git はこの点について非常に似通ったメカニズムを使用しています。ですが、おそらく実際に .gitignore ファイルを Mercurial リポジトリへコミットしたい、ということはないでしょう。 幸いなことに、 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 フラグを指定したため、 `notes'' 参照が表示されていますが、これは git-remote-hg が内部的に使用しているものなので、無視して構いません。 残りが期待していた内容です。 `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 で作成したコミットを git-remote-hg がプッシュして作成したものです。

ブランチとブックマーク

Git のブランチは1種類しかありません。コミットに合わせて移動する参照です。 Mercurial では、この種の参照は ``ブックマーク'' と呼ばれており、 Git のブランチとほぼ同じように振る舞います。

Mercurial の言う `ブランチ'' は Git のそれよりもっと重量級の概念です。 ブランチの上でチェンジセットが作成された場合、ブランチは チェンジセットと一緒に 記録されます。つまり、常にリポジトリの歴史に残ります。 `develop ブランチの上で作成されたコミットの例を次に示します。

$ hg log -l 1
changeset:   6:8f65e5e02793
branch:      develop
tag:         tip
user:        Ben Straub <[email protected]>
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 のブランチと非常によく似ていますが、一つ例外があります。 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 <[email protected]>
|  date:        Thu Aug 14 20:21:09 2014 -0700
|  summary:     A permanent change
|
| @  changeset:   6:8f65e5e02793
|/   branch:      develop
|    user:        Ben Straub <[email protected]>
|    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 <[email protected]>
| |  date:        Thu Aug 14 20:02:21 2014 -0700
| |  summary:     Merge remote-tracking branch 'origin/master'
[...]

``permanent'' という名前のブランチが、 7 とマークされたチェンジセットと一緒に記録されています。

Git 側では、いずれのタイプのブランチで作業をするのも変わりません。普段と同じように、チェックアウト、コミット、フェッチ、マージ、プル、プッシュが行えます。 一つ知っておくべきこととして、 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

チェンジセット 8910 が作成され、 permanent ブランチに紐付けられていますが、古いチェンジセットも残っています。 これは Mercurial を使用している他のチームメンバーを かなり 混乱させるので、できる限り避けましょう。

Mercurial のまとめ

Git と Mercurial は非常に似通っており、相互に作業してもほとんど苦になりません。 (一般的に推奨されているように)あなたのマシン上にある歴史を変更しないようにさえしていれば、相手側にあるのが Mercurial であることを意識する必要もありません。