mercurial
概要
- git のような分散バージョン管理ツール
- git でできることは大抵できる
git との違い
- 過去改変が git より面倒 (コミットメッセージの変更も含む)
- テキストデータだけでなく、バイナリファイル管理にも向いている (容量的に)
- add の挙動が異なり、一度 add すると、ずっと管理していてくれる (コミットの度に add しなくて済む)
- 個人的には git より、扱いが管理が簡単な気がする
- リビジョンをハッシュの他に、コミット番号でも管理するため、リビジョン指定がシンプル
インストール
apt からのインストール
$ sudo apt install mercurial
pip からのインストール
$ sudo -H pip3 install mercurial
- 最新版がインストールされる。
libbz2-dev
とlibffi-dev
が必要。
初期設定
- 設定
- ホームディレクトリに .hgrc を作成して編集する
[ui] username = ユーザ名 <メールアドレス>
設定ファイル
- ホームディレクトリに .hgrc として作成、編集する
- 基本としては「[]」で囲まれた項目と、「設定名 = 設定値」で構成される
項目 | 設定名 | 設定値 |
---|---|---|
ui | username | ユーザ名 <メールアドレス> |
merge | マージする際に差分を表示する外部ツール (diff3 あたりを書いておけば良さそう) | |
ignore | すべてのリポジトリに共通な無視リストのファイルパス | |
alias | エイリアス名 | mercurialのコマンド (例: ci = commit) |
extentions | エクステンション名 | なし(詳しくは エクステンションに記述) |
使い方
基本的には git と同じ
- リポジトリを作成 (hg init)
- 管理ファイルを追加 (hg add)
- コミット (hg commit)
以降は、2を必要に応じて行いつつ、3を繰り返す
コマンド
リポジトリの作成
$ hg init
ファイルの追加
$ hg add [ファイル]
- ファイルやディレクトリを指定しない場合、すべてのファイルが add される (ディレクトリもあれば再帰的に追加される)
- 管理ファイルの取り消し
$ hg revert [取り消したいファイル]
管理ファイルの状態
$ hg status
status
はst
でも可- ファイルの状態
- ?: 管理下にないファイル (add されていないファイル)
- A: 次回コミット時に追加されるファイル (add 済みのファイル)
- M: 既に管理下にあり、変更されたファイル
- R: 削除されたファイル (hg rm で削除したファイル)
- !: 存在しないファイル (hg rm をしないで削除されたファイル; mercurial 上では削除されていないファイル)
管理しないファイルを無視する場合
- エディタが出力するバックアップファイルやロックファイルなど、管理する予定もないファイルが状態に表示されると、非常に見づらくなる
- その場合は無視リスト (リポジトリのトップに .hgignore) を作成して対応する
- 1行目はリストをワイルドカードか、正規表現か、どちらの表記で書くかを指定する
glob
: ワイルドカードre
あるいはregexp
: Perl や Python の正規表現
- 2行目以降にワイルドカードか正規表現で示されたファイルのリストを書いていく
- 例
syntax: regexp \/?\.\~lock\..+ \/?\~\$.+ \.zip$
- ~lock. で始まるファイルを無視
- ~$ で始まるファイルを無視
- .zip ファイルを無視
ファイルの削除
- 実ファイルを削除せずに管理から外す (次回コミット時から管理しない)
$ hg forget <ファイル>
- 実ファイルを削除して管理から外す (次回コミット時から管理しない)
$ hg rm <ファイル>
rm
はremove
でも可
ファイルのコピー
$ hg cp <コピー元> <コピー先>
- ただし、コピー元の履歴まで引き継がれるわけではないので注意
cp
はcopy
でも可
ファイルの移動(リネーム)
$ hg mv <ファイル> <変更後の名前>
- 実体は copy+remove なので、元ファイルの履歴は消える
mv
はrename
でも可
コミット
- コミットメッセージを後から編集するコミット
$ hg commit
- 実行すると端末上でエディタが開かれるので、そこにコミットメッセージを入力する
- コミットメッセージとともにコミット
$ hg commit -m <コミットメッセージ>
- コミットメッセージを書いたファイルを指定してコミット
$ hg commit -l <コミットメッセージファイル
- 特定のファイルのみコミット
$ hg commit <コミットしたいファイル>
- デフォルトでは、一度 add したファイルは次から自動でコミット対象となっている
直前のリポジトリ操作の取り消し
$ hg rollback
- 直前のcommit, pull, import, unbundle を 1 回だけ取り消す
- 作業領域は変更しない
誤って複数のファイルをコミットした場合の取り消し
- mercurial では、一度
add
したものはremove
やforget
するまで常にトラッキングされる。 - 特定のファイルのみのコミットの場合、コミットの章で触れたように、
hg commit FILE
としてファイルを指定する。 - 指定を忘れて誤って全体をコミットしてしまった場合に戻す方法は
rollback
以外では以下の通りである (事前にMQ
エクステンションを有効にしておく必要あり)。
$ hg qimport -r REV_ID $ hg qrefresh FILE $ hg qfinish -a
REV_ID
: 修正したいリビジョン番号FILE
: 本来コミットするはずだったファイル群- これで特定のファイルのみのコミットの修正される。
qimport -r
の後に、hg qseries -s
やhg qdiff
などで誤ったリビジョンの確認をすると確実。
リビジョン表示
- リビジョン(履歴)を表示する
$ hg log [オプション]
- オプション
-r
: リビジョンを指定-l
: リビジョンの表示数 (指定がない場合は、すべて表示する)-v
: コミットメッセージ全文を表示する
- 表示例:
changeset: 1:96a83a913743 <- 2回目のコミットの「リビジョン番号:ハッシュ」 tag: tip <- タグ user: hoge <hoge@example.com> <- コミットしたユーザ date: Wed May 18 10:16:22 2016 +0900 <- コミット日時 summary: 2nd commit <- コミットメッセージのタイトル(1行目の内容) changeset: 0:0ed873115825 <- 1回目のコミットの「リビジョン番号:ハッシュ」 user: hoge <hoge@example.com> <- コミットしたユーザ date: Wed May 18 10:15:11 2016 +0900 <- コミット日時 summary: 1st commit <- コミットメッセージのタイトル(1行目の内容)
復元
- 特定のリビジョンのコミット状態に戻す
$ hg update [オプション] <リビジョン>
-C
をつけると作業領域の変更を破棄するupdate
はcheckout
でも可
- 特定のファイルのみ特定のリビジョンのコミット状態に戻す
$ hg revert [オプション] <ファイル>
-r <リビジョン>
: 特定のリビジョンのコミット状態に戻す (このオプションを付けない場合、直前のリビジョンに戻す)
- 特定のリビジョンにおけるファイルを別名で保存する
$ hg cat -r <リビジョン> -o <別名> <ファイル>
リビジョンの削除
$ hg strip <リビジョン>
- 実行後、バックアップが取られる
- 誤って削除したリビジョンを戻す
$ hg unbundle .hg/strip-backup/<ハッシュ>.hg
- ハッシュは、strip した直後にバックアップしたメッセージに表示されている
- メッセージを忘れた場合は、.hg/strip-backup 内にファイルがあるので、そこから探すしかない…。
ブランチ
- ブランチの新規作成
$ hg branch <ブランチ名>
- ブランチ作成すると、自動的に作成したブランチに移動する
- 作成したブランチからコミット前に別のブランチに移動すると、ブランチが削除される
- 現在のブランチ表示
$ hg branch
- ブランチ一覧表示
$ hg branches
- ブランチ間の移動
$ hg checkout <ブランチ名>
- ブランチのマージ
$ hg merge <マージしたいブランチ>
- マージする際は取り込み後に残るブランチにいる必要がある
- ブランチの削除
$ hg strip <削除したいブランチ>
リビジョンのブランチ間の移動
- rebase 拡張が必要なので、.hgrc の
[extensions]
以下に追記
rebase =
$ hg update <リビジョンの移動先ブランチ> $ hg rebase --source revision_s --dest revision_d --keep
revision_s
: 移動させたいリビジョンの開始番号 (指定した番号以降のリビジョンも一緒に移動する)revision_d
: 移動先のリビジョン番号 (指定されたリビジョンの後に、移動指定したリビジョンが取り付けられる)–keep
: デフォルトでは、リビジョン移動後に移動元のブランチが消されるため、このオプションを付けて消さないようにする
- 参考サイト: Mercurialの履歴改変を覚えたい - くろのて
特定のリビジョンを配布
- 特定のリビジョンの状態を USB メモリや Web 上で配布する場合に、.hg や .hgignore ファイルはつけたくない
- その場合、以下のコマンドを使う
$ hg archive [オプション] -r <リビジョン> <出力名>
-X 除外ファイル
: 除外ファイルを指定する- 例:
-X .hg*
(.hg や .hgignore を除外する)
- 出力名は名前だけだと、出力名のフォルダを新たに作成してその下にコピーするが、.zip や .tgz などの拡張子をつけると圧縮形式で出力してくれる
過去のコミットメッセージの変更方法
- エクステンションの histedit を使うので、使える状態にする
- ここではリビジョンの 1 と 2 を変更する例を紹介します
- 初期リポジトリ (例)
@ changeset: 3:c56f122d5366 | tag: tip | user: mumeiyamibito | date: Fri Oct 28 09:52:22 2016 +0900 | summary: 4th commit | o changeset: 2:315becd9db3f | user: mumeiyamibito | date: Fri Oct 28 09:52:12 2016 +0900 | summary: 3rd commit | o changeset: 1:88774b2a3443 | user: mumeiyamibito | date: Fri Oct 28 09:52:06 2016 +0900 | summary: 2nd commit | o changeset: 0:93daa7d382ae user: mumeiyamibito date: Fri Oct 28 09:52:00 2016 +0900 summary: 1st commit
- histedit の実行
$ hg histedit <リビジョン>
$ hg histedit 1
- エディタが立ち上がり、
pick <リビジョンのハッシュ> <リビジョン番号> <コミットメッセージ>
のリストが表示される
pick 88774b2a3443 1 2nd commit pick 315becd9db3f 2 3rd commit pick c56f122d5366 3 4th commit # Edit history between 88774b2a3443 and c56f122d5366 # # Commits are listed from least to most recent # # Commands: # # e, edit = use commit, but stop for amending # m, mess = edit commit message without changing commit content # p, pick = use commit # d, drop = remove commit from history # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description #
- 編集したいリビジョンの
pick
をedit
あるいはe
に変更する
edit 88774b2a3443 1 2nd commit edit 315becd9db3f 2 3rd commit pick c56f122d5366 3 4th commit # Edit history between 88774b2a3443 and c56f122d5366 # # Commits are listed from least to most recent # # Commands: # # e, edit = use commit, but stop for amending # m, mess = edit commit message without changing commit content # p, pick = use commit # d, drop = remove commit from history # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description #
- リビジョン 1 のコミットメッセージを変更する
$ hg histedit --continue
- いつものコミット時のエディタが起動するので、リビジョン 1 に付ける新しいコミットメッセージを記入する
- リビジョン 2 のコミットメッセージを変更する
$ hg histedit --continue
- いつものコミット時のエディタが起動するので、リビジョン 2 に付ける新しいコミットメッセージを記入する
複数のリポジトリの統合 (マージ)
- 複数の別々のリポジトリで管理していたファイルを、それぞれの履歴を残しつつ、マージする方法
$ hg pull -f /path/to/repository $ hg merge $ hg commit
/path/to/repository
: 統合したい別のリポジトリのパスpull
を使っているが、ローカルのリポジトリでも問題ない- 実行すると、別リポジトリがブランチとして履歴に加わり、統合される (デフォルトのタイムラインに追加されるわけではない)
- 参考サイト
特定ファイルのみのリポジトリを別に作りたい
- 上記の複数リポジトリの統一とは逆に、特定ファイルのみの別リポジトリにして管理する方法
$ hg convert /path/to/original_repository /path/to/new_repository --filemap map.txt
/path/to/original_repository
: コピー元のリポジトリのパス/path/to/new_repository
: 新たに作成する特定ファイルのみの別リポジトリのパスmap.txt
: フィルタ条件を書き込んだファイル
- map.txt
include INCLUDE_FILE_PATH exclude EXCLUDE_FILE_PATH rename ORIGINAL_PATH RENAMED_PATH
INCLUDE_FILE_PATH
: リポジトリコピーの際に、コピーするファイルのパス (リポジトリのトップパスがベース)EXCLUDE_FILE_PATH
: リポジトリコピーの際に、外したいファイルのパス (リポジトリのトップパスがベース)ORIGINAL_PATH
: リポジトリコピーの際に、名前を変更したいファイルのパス (リポジトリのトップパスがベース)RENAMED_PATH
: リポジトリコピーの際に、名前変更後のパス (リポジトリのトップパスがベース)include
もexclude
も指定していない場合、すべてのファイルがコピーされる。rename
は事前にinclude
しておく必要あり (rename
だけではコピーされない)- これらの指定は 1 ファイルにつき、1 行使う (
include
やexclude
の後に、ファイルのパスを続けて指定するとエラーが出る。) - 例: リポジトリ内の
class/class1.py
とclass/class2.py
のみのリポジトリを作成する (class1.py
はリポジトリのトップにclass_main.py
として配置する)
include class/class1.py include class/class2.py rename class/class1.py class_main.py
- 事前にエクステンションを有効化する必要があるため、
.hgrc
の[extensions]
以下に追記する。
[extensions] convert=
- 参考サイト:
リポジトリを修復したい
- 何らかの処理によって、mercurial の管理ディレクトリ内のデータが破損した場合、mercurial の操作をしようとすると
abort: data/FILE.i@XXXXXXXXXXXX: no match found!
というエラーが現れることがある (FILE
はその名前のファイルのトラッキングデータ)。 - 修復するには以下のコマンドを実行すると復旧できる可能性がある
$ hg convert --config convert.hg.ignoreerrors=True SOURCE_REPO DEST_REPO
SOURCE_REPO
: 修復したいリポジトリ (mercurial で管理しているディレクトリ)DEST_REPO
: 修復後のリポジトリの出力先
SSH が利用できるリモートサーバとの連携
- mercurial のサーバ構築のサイトの多くは Apache を使った方法を紹介しているが、ただバージョン管理のストレージとして使いたい場合には大掛かり過ぎる気がする (Apache を設定すれば、ウェブ上で公開することが可能である)。ここでは、バージョン管理のストレージとして使う場合を想定して紹介する。
- サーバ構築
- SSH サーバ + mercurial がインストールされている環境を構築すれば良い (構築方法は他のサイトを参照)。
- クライアント側の設定 (オプション)
- SSH を公開鍵設定やポート変更をしている場合、
~/.hgrc
の[ui]
に SSH の設定を追記することで利用できる。
[ui] ssh = ssh -i PRIVATE_KEY -p PORT
PRIVATE_KEY
: 秘密鍵のパスPORT
: ポート番号
- 使い方 (クライアント側)
- リモートサーバにリポジトリを作成
$ hg clone . ssh://USER_NAME@SERVER_NAME/path/to/new_repository
USER_NAME
: SSH のユーザ名SERVER_NAME
: SSH のサーバ名 (IP アドレス)path/to/new_repository
: サーバ内のパス (USER_NAME
が指定されているのであれば、/home/USER_NAME
が起点になる)
- プッシュ
$ hg push ssh://USER_NAME@SERVER_NAME/path/to/repository
USER_NAME
: SSH のユーザ名SERVER_NAME
: SSH のサーバ名 (IP アドレス)path/to/repository
: サーバ内のパス (USER_NAME
が指定されているのであれば、/home/USER_NAME
が起点になる)
- クローン
$ hg clone ssh://USER_NAME@SERVER_NAME/path/to/repository
USER_NAME
: SSH のユーザ名SERVER_NAME
: SSH のサーバ名 (IP アドレス)path/to/repository
: サーバ内のパス (USER_NAME
が指定されているのであれば、/home/USER_NAME
が起点になる)
- プル
$ hg pull ssh://USER_NAME@SERVER_NAME/path/to/repository
USER_NAME
: SSH のユーザ名SERVER_NAME
: SSH のサーバ名 (IP アドレス)path/to/repository
: サーバ内のパス (USER_NAME
が指定されているのであれば、/home/USER_NAME
が起点になる)
リポジトリの root ディレクトリパスを調べる
- シェルスクリプトなどに使えるかも。
$ hg root
エクステンション
- mercurial を快適に使うための拡張パック
- 大抵はインストールされているものの、有効になっていない
- 有効にするには .hgrc の [extentions] 以下にエクステンション名を入れる
[extentions] エクステンション名 = エクステンション名 = エクステンションのパス
- 外部エクステンションの場合、エクステンションのパスが必要
- 無銘闇人が導入しているエクステンション
- graphlog: リビジョン一覧表示の際に、左端にリビジョンのツリー構造(ブランチの分かれ具合など)をアスキーアートで表示
- 使う場合は、
hg log
と同様の感覚で、hg glog
。 - エイリアスで log を glog に置き換えてもいいかもしれない
- mq: 変更内容をパッチ化する (過去改変などで使う)
- convert: git など他の分散バージョン管理システムのリポジトリを mercurial 用に変換する
$ hg convert <GITリポジトリ> <新リポジトリ>
- color: diff や status に色を付けて表示
[color]
にmode = auto
- shelve: 作業領域を一時的に退避する (git の stash)
- eol: 改行コードの管理
- progress: コミット時に進捗状況を表示
[progress]
にdeley = 1.5
-
- 過去のコミットメッセージを変更するなど過去改変をする拡張機能
- 多くのサイトで、hg clone で導入して、.hgrc に
histedit = /path/to/histedit/hg_histedit.py
と記述する方法が書かれているが、Ver. 2.3 以降はデフォルトでインストールされている (ちなみに Ubuntu 16.04 相当では、Ver. 3.7.3 なので mercurial インストールするだけでこのエクステンションはインストールされている)- Ver. 2.3 以降は
histedit =
を .hgrc の[extensions]
以下に記述するだけで良い
- zipdoc: docx などの XML 形式の Word を管理する場合、バージョン管理用のフォルダの肥大化を抑制
- Word の docx は XML を zip で固めたものであり、mercurial ではバイナリとして認識されるため、差分ではなく、zip を保存することでバージョン管理している。このエクステンションは docx などをコミット・チェックアウトする際に展開・圧縮して差分で管理するようにする。
- 標準ではインストールされていないので、 ここ から入手する
- .hgrc
- [extensions]:
zipdoc = /path/to/zipdoc.py
を追記する (/path/to/zipdoc.py は zipdoc.py のパスを入力) - [encode] と [decode] の項目を作成する。詳しくは以下のサイトを参考にする。
リポジトリを git リポジトリに変換する
- 変換プログラムをダウンロードする。
$ git clone http://repo.or.cz/r/fast-export.git fast-export
- 転送先の git リポジトリ (空)
NEW_GIT_REPO
を作成する。
$ mkdir NEW_GIT_REPO $ cd NEW_GIT_REPO $ git init
- 変換する。
$ ../fast-export/hg-fast-export.sh -r HG_REPO_PATH
HG_REPO_PATH
: 変換したい mercurial のリポジトリパス
- 参考サイト: mercurial リポジトリを git に移行する
Tips
一部のサブコマンドで pager を無効化する
hg status
やhg log
などの結果はデフォルトでは、less
コマンド (pager) のように表示される。- これらの結果をプロンプトに反映させたい場合や、端末上にその結果を残したい (pager は q キーを押すと、結果が消える) 場合は、設定ファイル
$HOME/.hgrc
に以下を追記すると良い。
[pager] ignore = SUBCOMMAND [, SUBCOMMND, ...]
SUBCOMMAND
: pager を無効化したいサブコマンド (log
やstatus
など hg の後に付けるキーワード)- 複数ある場合は、カンマで区切る
- 参考サイト: PagerExtension - Mercurial
- 一時的に無効化したい場合は、
–pager false
を各コマンドの後ろにでも付けておけば良い。- 例:
$ hg log --pager false