Windows+TortoiseHgで始めるMercurial(hgコマンドをTortoiseHgで)

この記事は1.1.xを基準にしていて古いので、2.x対応版に書きなおした。

  1. 設定 => 2.x版はこちら
  2. hgコマンドをTortoiseHgで => 2.x版はこちら
  3. ブランチ・リポジトリ間の連携 => 2.x版はこちら
  4. 間違えたときのやり直し => 2.x版はこちら
  5. bitbucketの使い方(あとで書く)=>2.x版はこちら

リポジトリの作成

Mercurialリポジトリ作成方法には2通りあります。

空のリポジトリを作成

新しくバージョン管理を開始する場合に使います。リポジトリを置くディレクトリ上で右クリックして、

「TortoiseHg」->「ここにリポジトリを作成」

を選択します。

「TortoiseHgリポジトリ作成」ダイアログで、リポジトリ作成ディレクトリが入力されていることを確認したら、「作成」ボタンをクリックします。

この時、日本語を含むディレクトリを指定するのは止めましょう。Mercurialは日本語の扱いに不安点があるので、余計なトラブルを避けた方が得策です。

「.hg」ディレクトリと「.hgignore」が作成されれば成功です。

既存のリポジトリをクローン

既存のリポジトリがあり、それをもとにバージョン管理する場合に使います。リポジトリを置くディレクトリ上で右クリックして、

「TortoiseHg」->「リポジトリのクローン」

を選択します。

ダイアログが表示されるので、クローン元にリポジトリのURLまたはディレクトリを入力して、「クローン」ボタンをクリックしましょう。
ここでは例として、https://bitbucket.org/toruuetani/hglight-for-traclightning3 をターゲットにします。

図のようにコンテンツが生成されれば成功です。

.hgignoreとは何か?

バージョン管理の対象としないファイルを指定するものです。中間成果物や、環境依存のファイルなどを指定します。ワイルドカード正規表現を指定できるので、できるだけ無駄な記述を省きましょう。

syntax: glob
*.pyc
*.javac

最低限覚えること

Mercurialで最低限覚えておかなければならない操作には、以下のような操作があります。これだけ覚えておけばとりあえずバージョン管理が可能です。

  • 更新 / hg update
  • コミット/ hg commit
更新

ある特定の時点にファイルを更新します。ログビューアで更新したいリビジョンを右クリックして、更新を選択します。

表示されたダイアログで「更新」をクリックします。現時点行っている変更を破棄しても構わない場合、「ローカルの変更を破棄する」をチェックしてください。

ある程度変更を重ねてからやり直したくなった場合に便利です。

コミット

ファイルを変更したら、エクスプローラ上で右クリックして、「Hg コミット」を選択します。

コミットダイアログで、変更に含めるファイルを選択してコメントを記述しましょう。慣例として、1行目は変更の概要を記述します。必要であれば、3行目以降に詳細を記述してください。TracなどのITSが用意されいてるなら、関連するチケットの番号を記述しておくべきです。ITSの機能により、自動的にチケットにコメントが追記されます。もしそういう仕組みがないのなら、今すぐセットアップしましょう。

Windows+TortoiseHgで始めるMercurial(設定編)

ちょっとMercurialのHowToを書く必要が出てきたので書いてみる。

この記事は1.1.xを基準にしていて古いので、2.x対応版に書きなおした。

  1. 設定 => 2.x版はこちら
  2. hgコマンドをTortoiseHgで => 2.x版はこちら
  3. ブランチ・リポジトリ間の連携 => 2.x版はこちら
  4. 間違えたときのやり直し => 2.x版はこちら
  5. bitbucketの使い方 =>2.x版はこちら

TortoiseHgとは何か?

MercurialGUIツールです。Windows標準のファイラーであるエクスプローラを拡張して、GUIでバージョン管理が出来るようにしてくれます。
同時にhgコマンドも使えるようにしてくれるので、Mercurialをインストールするよりお勧めです。

 ダウンロードはこちらから -> TortoiseHg
 現在の最新版は tortoisehg-1.1.9.1-hg-1.7.5-x86.msi です。

 

TortoiseHgのインストール

 

基本的にデフォルトインストールで構いません。エクスプローラ上で右クリックして、TortoiseHgと表示されたら成功です。
※1.1.8からメニューが自動的に日本語化されるようになりました。(参照)LANG環境変数なしで言語環境を検出してくれるようになったので、LANG環境変数の設定は必要ありません。

フォント設定の変更

インストールした状態だと、ログビューアのコミットログ表示部分の日本語部分が残念なことになります。

TortoiseHg(Mecrurial)は次のファイルに設定を保存します。

%USERPROFILE%\mercurial.ini

ここに以下のようにフォントを設定します(メイリオの場合)。

[gtools]
fontcomment = メイリオ 10
fontdiff = メイリオ 10
fontlist = メイリオ 9

以下のように設定したフォントでログビューアが表示されたら成功です。

マージツールの設定

標準ツールkdiffでもマージできますが、WinMerge 日本語版の方が使いやすいので設定しておきましょう。mercurial.iniに以下の設定を追加してください。

[merge-tools]
winmergeu.args=/e /ub /dl other /dr local $other $local $output
winmergeu.regkey=Software\Thingamahoochie\WinMerge
winmergeu.regname=Executable
winmergeu.fixeol=True
winmergeu.checkchanged=True
winmergeu.gui=True

[extdiff]
cmd.wmdiff = C:\Program Files\WinMerge\WinMergeU.exe
opts.wmdiff = /r /e /x /ub

[ui]
merge = winmergeu

[tortoisehg]
vdiff = wmdiff

次にTortoiseHgのグローバル設定を変更します。(エクスプローラ右クリック -> TortoiseHg -> グローバル設定)

以下のように設定してください。

  • 「3-way マージツール」=「winmergeu」
  • GUI差分表示ツール」=「wmdiff」

win32mbcsの設定

Mercurialは非常に使いやすいVCSですが、日本語ファイルパスの扱いだけはdisられています。
なぜかと言うと、Mercurialはファイルパスをバイナリで格納するため、日本語Windowsで格納した日本語ファイルをLinuxで取り出せません。逆も同じです。日本語WindowsでファイルパスはShiftJISでエンコードされ、LinuxではUTF8でエンコードされるからです。また、ShiftJISには0x5C問題があり、Mercurialは正しくパスを格納できません。
ただ、Windows環境だけに限定するなら回避策があり、win32mbcs拡張を有効にすることで問題なくなります。

win32mbcsを有効にするためには、mercurial.iniに次の設定を追加します。

[extensions]
win32mbcs = 

あるいはTortoiseHgのグローバル設定で、Extensions設定からwin32mbcsにチェックします。

プロキシ設定

プロキシもTortoiseHgのグローバル設定で設定できます。

hglightをTracLightning 3.0に対応してみた。

自己流でTracMercurialをセットアップするのも面倒なので、hglightをforkしてTracLightning 3.0に対応してみた。こちらからどうぞ。インストール手順とかは本家と同じです。

本家と違うのはこれくらい。

今後やってみたいのはこれくらい。

  • trac-post-commit-hook対応
    • ないと結構やってられない。
    • trac 0.11だと、チケットの更新日時がミリ秒まで持ってない?から多数のチェンジセットをpushすると、たまにフックに失敗してコメントが付与されないことがある。 trac 0.12でミリ秒まで持つようになったらしいので、それはなくなるはず。
  • プロジェクト生成時、既存リポジトリからcloneできるようにする。

bitbucketの使い方メモ

基本的なこと、サインインとかpush/pull/forkは大抵書いてあるけど、forkしてから先の事とかを書いてるサイトが見つからなかったので、メモしておく。

fork元の変更をpullする。

bitbucket上でやるのかと思ってたけど、違うみたい。

まずはforkしたリポジトリのローカルリポジトリで、fork元のURLを指定してpullする。その後pushするだけ。

CD WORK_DIR
hg pull https://yourname@bitbucket.org/fork_source
hg push

もちろんローカルだけに変更を留めておきたいなら、pushしなければいい。

trac-post-commit-hookみたいなことをやる。

デフォルト設定だとできないので、以下のURLを参考にする。

http://confluence.atlassian.com/display/BITBUCKET/Setting+Up+the+Bitbucket+Issues+Service

まずはリポジトリのadminタブwクリックする。そうすると「Additional options/settings」というブロックが表示されるので、「Services」をクリックする。

「Services Administration」というブロックで、コンボボックスから「Issues」を選択して「Add service」をクリックする。

これでコミットログに以下のように記述すればいい。はずなんだけど、たまに失敗してる?

fixes #4711 #チケットのクローズ
reopening #4711 #チケットの再オープン
refs #4711 #チケットへの参照

TracLightningでMercurialを使ってみた

 サーバリプレースのついでに、自己流でセットアップしたTrac0.10からTracLightning2.3.2に移行してみた。ついでにTracをmod_wsgiで動かしてみたり、Mercurialと連携させてみたり、チケットの自動クローズとかできるようにしてみたのでメモしておく。

環境はこんな感じ。

TracLightningをmod_wsgiで動かす

参考URL

これは参考URLのまま。
1.2つ目のURLからmod_wsgi.soをダウンロードして、C:\TracLight\CollabNetSVN\httpd\modulesにコピー
2.C:\TracLight\CollabNetSVN\httpd\conf\httpd.confの書き換え

--- a/CollabNetSVN/httpd/conf/httpd.conf	Fri Nov 20 14:55:06 2009 +0900
+++ b/CollabNetSVN/httpd/conf/httpd.conf	Fri Nov 20 15:05:18 2009 +0900
@@ -496,7 +496,7 @@
 LoadFile "../../python/python25.dll"
 LoadModule authz_svn_module modules/mod_authz_svn.so
 LoadModule dav_svn_module modules/mod_dav_svn.so
-LoadModule python_module modules/mod_python.so
+#LoadModule python_module modules/mod_python.so
 #LoadModule fcgid_module modules/mod_fcgid.so
 
 
@@ -534,14 +534,19 @@
 # PythonDebug On 
 
 #DefaultInitEnv TRAC_ENV_PARENT_DIR "C:\TracLight\projects\trac"
-ScriptAlias /trac "C:\TracLight\CollabNetSVN\httpd\cgi-bin\trac.cgi"
+#ScriptAlias /trac "C:\TracLight\CollabNetSVN\httpd\cgi-bin\trac.cgi"
+LoadModule wsgi_module modules/mod_wsgi.so
+WSGIScriptAlias /trac "C:/TracLight/CollabNetSVN/httpd/cgi-bin/trac.wsgi"
 
 <Location "/trac">
-  SetHandler mod_python
-  PythonHandler trac.web.modpython_frontend
-  PythonOption TracEnvParentDir "C:\TracLight\projects\trac"
-  PythonOption TracUriRoot /trac
-  PythonOption PYTHON_EGG_CACHE "C:\TracLight\projects\.egg-cache"
+#  SetHandler mod_python
+#  PythonHandler trac.web.modpython_frontend
+#  PythonOption TracEnvParentDir "C:\TracLight\projects\trac"
+#  PythonOption TracUriRoot /trac
+#  PythonOption PYTHON_EGG_CACHE "C:\TracLight\projects\.egg-cache"
+  WSGIApplicationGroup %{GLOBAL}
+  Order deny,allow
+  Allow from all
 </Location>
 
 <Location "/svn/">

3.C:\TracLight\CollabNetSVN\httpd\cgi-bin\trac.wsgiを作成

#!C:\TracLight/python/python.exe
# -*- coding: utf-8 -*-
#

import sys
sys.stdout = sys.stderr

import os
os.environ['TRAC_ENV_PARENT_DIR'] = 'C:/TracLight/projects/trac'
os.environ['PYTHON_EGG_CACHE'] = 'C:/TracLight/projects/.egg-cache'

import trac.web.main

application = trac.web.main.dispatch_request

Mercurialとの連携

参考URL

Mercurialはバイナリインストーラは使わず、easy_installでインストールする。でないと、TracMercurialを入れても「Unsupported version control system "hg"」と言われてしまう。

なので、まずはeasy_installから。ez_setup.pyをダウンロードして、コマンドラインから以下のコマンドを実行する。

python ez_setup.py


次に、Mercurialをコンパイルできるようにするため、2つ目のURLを参考にMinGWをインストール。
環境変数PATHに以下の設定を追加して、

%PATH%;C:\MinGW\bin;

C:\TracLight\python\Lib\distutilsの中に「distutils.cfg」というファイルを作成し、そのファイルに下記を記述して保存。

[build]
compiler=mingw32

あとは以下のコマンドでMercurialがインストールできる。

easy_install mercurial


次はTracMercurial – The Trac Project

easy_install -UZ http://svn.edgewall.com/repos/trac/sandbox/mercurial-plugin-0.11

SVNでチェックアウトできない場合は、ソースをダウンロードして、以下のコマンドでインストールする。

python setup.py install


最後にtrac.iniを変更する。
Mercurialリポジトリを「C:\TracLight\projects\hg」に作ることにして、「C:\TracLight\projects\hg\SampleProject」にリポジトリを作成。

--- a/projects/trac/SampleProject/conf/trac.ini	Fri Nov 20 15:05:18 2009 +0900
+++ b/projects/trac/SampleProject/conf/trac.ini	Fri Nov 20 16:59:36 2009 +0900
@@ -29,7 +29,8 @@
 resolve.permissions = TICKET_MODIFY
 
 [trac]
-repository_dir = C:\TracLight\projects/svn/SampleProject
+repository_dir = C:\TracLight\projects/hg/SampleProject
+repository_type = hg
 
 authz_module_name = SampleProject 
 [mainnav]
@@ -53,6 +54,7 @@
 timingandestimationplugin.ticket_daemon.timetrackingticketobserver = disabled
 timingandestimationplugin.ticket_webui.ticketwebuiaddon = disabled
 timingandestimationplugin.webui.timingestimationandbillingpage = disabled
+tracext.hg.* = enabled
 
 [ticket-custom]
 due_assign = text
@@ -73,8 +75,11 @@
 private_wikis = SECRET
 
 #[searchhyperestraier]
 #index_path = C:\TracLight\casket
 #replace_left = C:\TracLight\rep
 #url_left = /
 
+[hg]
+show_rev = yes
+node_format = short

チェンジセット1がない、とエラーにはなるが、コミットすれば問題なく使えるようになる。

チケットの自動クローズ

参考URL

これが一番苦労した。なぜかバッチファイルはどんなものを指定しても実行してくれない。権限の関係?

悩んでても解決しないので、1個目のURLを参考に別の方法でアプローチする。


まずMercurialリポジトリをhttpで公開する。
2個目と3個目のURLを参考に、C:/TracLight/CollabNetSVN/httpd/cgi-bin/hgweb.wsgiを作成して、

import os
os.environ['HGENCODING'] = 'UTF-8'

import mercurial.hg as hg
from mercurial.ui import ui
from mercurial.hgweb.hgweb_mod import hgweb
from mercurial.hgweb.hgwebdir_mod import hgwebdir
from mercurial.hgweb.request import wsgiapplication

def listdir_dironly(base_dir):
    results = []    # prepare for failure
    for root, dirs, files in os.walk(base_dir):
        results = map(lambda d: os.path.join(root, d), dirs)
        dirs[:] = []
    results.sort()
    return results

def get_repo_for_path(path):
    return hg.repository(ui(interactive=False,
                            report_untrusted=False),
                         path=path)

def isrepo(path):
    if False:   # too redundant
        try:
            get_repo_for_path(path)
            return True
        except hg.RepoError:
            return False
    else:
        return os.path.isdir(os.path.join(path, '.hg'))

def make_hgweb_maker(path):
    return lambda: hgweb(path, os.path.split(path)[1])
    #return lambda: hgweb(get_repo_for_path(path))

def make_hgwebdir_maker(path):
    dirs = listdir_dironly(path)
    repos = [(os.path.split(dir)[1], dir) for dir in dirs]
    return lambda: hgwebdir(repos)

def hgweb_wsgiapp(path):
    if isrepo(path):
        return wsgiapplication(make_hgweb_maker(path))
    else:
        return wsgiapplication(make_hgwebdir_maker(path))

# for WSGI
def application(environ, start_response):
    def filter_headers(status, response_headers):
        # stringify header content
        headers = [(key, str(value)) for key, value in response_headers]
        return start_response(status, headers)
    def error_dialog(message):
        headers = [('Content-Type',   'text/plain'),
                   ('Content-Length', str(len(message)))]
        start_response('500 Internal Server Error', headers)
        return [message]

    if 'hgweb.reposdir' in environ:
        reposdir = environ['hgweb.reposdir']
    else:
        return error_dialog("You must specify 'hgweb.reposdir' environment")

    wsgiapp = hgweb_wsgiapp(reposdir)
    return wsgiapp(environ, filter_headers)

# for CGI
if os.environ.get('GATEWAY_INTERFACE', '').startswith('CGI/'):
    import cgitb
    cgitb.enable()

    import mercurial.hgweb.wsgicgi as wsgicgi
    wsgicgi.launch(hgweb_wsgiapp('/path/to/repos'))

さらにhttpd.confを修正する。この時点でHTTP経由でリポジトリが参照できるはず。

--- a/CollabNetSVN/httpd/conf/httpd.conf	Fri Nov 20 16:59:36 2009 +0900
+++ b/CollabNetSVN/httpd/conf/httpd.conf	Fri Nov 20 17:30:12 2009 +0900
@@ -588,3 +588,19 @@
   ProxyPass        http://127.0.0.1:8010/hudson
   ProxyPassReverse http://127.0.0.1:8010/hudson
 </Location>
+
+WSGIScriptAlias /hg "C:/TracLight/CollabNetSVN/httpd/cgi-bin/hgweb.wsgi"
+
+<Location "/hg">
+  WSGIApplicationGroup %{GLOBAL}
+  SetEnv hgweb.reposdir "C:/TracLight/projects/hg/"
+  Order deny,allow
+  Allow from all
+
+  AuthType Digest
+  AuthName trac
+  AuthUserFile "C:\TracLight\projects\trac.htdigest"
+  <LimitExcept GET PROPFIND OPTIONS REPORT>
+    Require valid-user
+  </LimitExcept>
+</Location>

次に4個目のURLを参考に、C:\TracLight\python\Lib\site-packages\hg_post_commit.pyを作成する。これは複数リポジトリがあってもいいように、少し修正してある。

# -*- encoding: utf-8 -*-

hook_script = 'C:/TracLight/python-lib/trac/contrib/trac-post-commit-hook'

# style:: 'short', 'long', or 'number'
changeset_id_style = 'short'

import os

def invoke_trac_hook(trac_env, rev):
    def _make_smart_rev(trac_env, rev):
        if changeset_id_style == 'long':
            return rev
        else:
            import trac.env
            env = trac.env.open_environment(trac_env)
            # instance of mercurial.hg.repository
            repo = env.get_repository().repo
            ctx = repo.changectx(rev)
            if changeset_id_style == 'short':
                import mercurial.node
                return mercurial.node.short(ctx.node())
            else:   # 'number'
                return str(ctx.rev())

    smart_rev = _make_smart_rev(trac_env, rev)

    f = open(hook_script, 'r')
    try:
        import imp
        ext = os.path.splitext(hook_script)[1]
        m = imp.load_module('trac_hook', f, f.name, (ext, f.mode, imp.PY_SOURCE))
        m.CommitHook(project=trac_env, rev=smart_rev)
    finally:
        f.close()

def hook(ui, repo, **kwargs):
    hg_dir, _ = os.path.split(repo.path)
    hg_root_dir, project_name = os.path.split(hg_dir)
    trac_dir = os.path.join(os.path.join(hg_root_dir, os.path.pardir, 'trac', project_name))

    if 'PYTHON_EGG_CACHE' not in os.environ:
        os.environ['PYTHON_EGG_CACHE'] = os.path.join(trac_dir, '.egg-cache')

    invoke_trac_hook(trac_dir, kwargs.get('node'))

最後に「C:\TracLight\projects\hg\SampleProject\.hg\hgrc」を作成して、上記のスクリプトを指定する。

[hooks]
incoming.trac = python:hg_post_commit.hook

[web]
push_ssl = false
allow_push = *
deny_push = unauthenticated_user

あとはSVNのときと同じようにコミットしてpushすれば、チケットが自動クローズされる。