学習・研究ファイルのリポジトリ化について

まとめ

概要

個人PCで作業を行なう場合でも,大切なファイル(ディレクトリ)は リモートリポジトリにマスターファイルを置き,つねに同期を取りながら バックアップのある状態で作業する。

また,修正履歴を全て保存できるので,「ファイル名を変えてバックアップ」 のようなことを一切せずに済むようになる。

この操作にはSCMソフトウェア Mercurial を使用するので,あらかじめインストールしておく。

リポジトリを利用したファイル操作の流れ

Image of Using Repository

  1. 事前準備
  2. 日常作業の流れ

Mercurialの準備

サーバ接続鍵の作成

研究室用の共有サーバを www.yatex.org 上に作成した。 ここに繋ぐための準備手順を示す。そのためには公開鍵認証用の 鍵ファイルを作成する。計算機故障等でなくすと困るのでroyで作成して それを手元計算機にコピーするようにする。

  1. 接続用公開鍵の作成

    royにログインし,以下のようにコマンド起動する。 なお,インターネット演習でこの作業をした場合は不要。

    ssh-keygen
    Generating public/private rsa key pair.
    Enter passphrase (empty for no passphrase): パスフレーズ入力
    Enter same passphrase again: : パスフレーズ入力
    

    パスフレーズは15〜20文字程度のすばやく打てるものを付ける。

  2. 鍵ファイルのコピー

    手元の計算機にroy上の鍵ファイルをコピーする。

    scp c1yyxxx@roy.e.koeki-u.ac.jp:.ssh/id_rsa'*' ~/.ssh
    

    c1yyxxx にはroyでの自分のユーザ名を入れる。

  3. サーバへの鍵ファイル登録

    作成してコピーしてきた ~/.ssh/id_rsa.pub ファイルを広瀬宛てに送る。これを読んでいる時点で完了しているはず。

  4. サーバ接続情報の登録

    手元の計算機の ~/.ssh/config に以下の記述を追加する。

    Host labosv
      HostName	www.yatex.org
      User		koeki
      Port		2422
      AddressFamily	inet
    
  5. 接続実験

    上記手順全て完了したら,手元の計算機の端末から接続できるか確認する。

    ssh labosv hostname
    Enter passphrase for key '/home/taro/.ssh/id_rsa': (パスフレーズ入力)
    www.yatex.org
    

  6. サーバ上の個人ディレクトリ作成

    上記のコマンド実効に成功したら,サーバ上に自分のディレクトリを 作成する。

    ssh labosv mkdir taro            # 自分のユーザ名
    

リポジトリの準備

作業ディレクトリのローカルリポジトリ化

保存したい作業ディレクトリを決めて, そこでMercurialリポジトリを構築する。ここでは ~/hoge をそのディレクトリとする。

cd hoge
cat > .hgignore<<_EOF_
syntax: glob
*.aux
*.log
*.toc
*.o
*~
\#*
tmp/
_EOF_

hg init; hg ci -m init -A

続けて,手元のリポジトリ(.)をサーバ上に複製する。 複製する先のディレクトリ名を最後に指定するが, これはサーバ上の自分用ディレクトリ下の名前にする。

hg clone . ssh://labosv/taro/foo

リモート上に作成したリポジトリをさらに手元に複製する。

cd          # ホームディレクトリに戻る
hg clone ssh://labosv/taro/foo

これで ~/foo に,リモートリポジトリを 原本としたクローンリポジトリができる。最初の作業ディレクトリ (この例では ~/hoge)と中味を比べる。

ls hoge
ls foo

完全に同じであることが確認できたら元のディレクトリは消してしまってよい。

rm -rf hoge         # これをやらずに残しておいてもよい

以後 ~/foo で編集作業する準備ができた。

日常の編集操作

リポジトリ内でのコマンドライン操作

Emacsでの操作

Emacsでファイルを編集した場合は,ある程度の修正ができたところで コミット(C-x v v)や,push (C-c h >) をする。

コミットすると,ログ記述バッファが開くので,やった作業の簡潔な説明を書き, C-c C-c で確定する。

複数ホストでの編集

一度サーバリポジトリに登録しておけば,作業の続きをネットに繋がった 別ホストで行なえる。たとえば,royやpanで行なう例を示す。

Repository Tree

大まかな流れとしては,roy/panなどにログイン後, サーバからリポジトリを複製する。その後は以下の流れで作業を行なう。

  1. サーバ上の更新をpullする(hg pull -uv)
  2. 複製したリポジトリ内で作業しコミットをこまめに行なう
  3. 切りのよいところでpushする(hg push)

必ず「pullしてから作業開始」と, 「作業の最後にpush」を忘れないようにする。

なお,pullのときに他のリポジトリを指定すると,そこから更新を 直接取得できる。たとえば,royのtaro/ディレクトリで作業した 結果をサーバにpushしていなくても,たとえばmypcから,

cd ~/foo
hg pull -uv ssh://roy/foo

とすると更新をmypcに取得でき,さらにこれを

hg push

するとサーバに更新を送り込むことができる。

マージと衝突

マージ

[サーバリポジトリ]
  |   |    |
  |   |   複製C
  |  複製B
複製A

上図のような関係で運用していて,複製Aで行なった更新をサーバにpushし, 次の日,複製Bで作業を始めるときにpullし忘れてから作業し始めたとする。 すると,Bでコミットしたものをpushするときに以下のようになる。

hg push
pushing to ssh://labosv/taro/foo
searching for changes
abort: push creates new remote head 38a9b881abe5!
(you should pull and merge or use push -f to force)

先にAでの更新を取り込んでおくべきだった。この場合はあとで Aの更新を取り込み,マージする。

hg pull -uv
pulling from ssh://labosv/taro/foo
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)

このようにしたら hg merge でマージ(修正の併合)を行なう。

hg merge
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)

don't forget とあるように,コミットを忘れず行なう。

hg ci -m merged

衝突と解決

マージすべきときに,同じ箇所に違う修正を施していると衝突が起こる。

hg merge
merging hello.c
merge: warning: conflicts during merge
merging hello.c failed!
0 files updated, 0 files merged, 0 files removed, 1 files unresolved
use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
hg resolve -l          # 衝突ファイル一覧を得る
U hello.c

ファイルを開くと衝突箇所に `<<<<<<<' というマークが付く。2つの修正候補のどちらかを残して他を消す。

int main(int argc, char **argv)
{
<<<<<<< /tmp/mh2/hello.c
        printf("HELLO, WORLD!\n");
======= 
        printf("Hello, world!\n");
>>>>>>> /tmp/hello.c~other.WW1aE0
        return 0;
}

ここでは下(赤)の方を選んだとして,

int main(int argc, char **argv)
{
	printf("Hello, world!\n");
        return 0;
}

のように修正し,修正済みマークを `hg resolve -m' で付け,コミットする。

hg resolve -m hello.c
hg resolve -l
R hello.c
hg ci -m Resolved

ssh-agentの利用

リモートサーバへの接続時のパスフレーズ入力を代理で済ませてくれる ソフトウェア ssh-agent を使うと頻繁なpush/pullが楽になる。 ~/.ssh/id_rsa ファイルを用意し終えた状態で,仮想端末から ssh-add を起動し,以下のようなパスフレーズ入力プロンプトが出たら 既に ssh-agent が常駐している。

ssh-add
Enter passphrase for /home/taro/.ssh/id_rsa

このままパスフレーズを入れておけば,次回の公開鍵認証の パスフレーズ入力が省略される。

ssh-add で無反応の場合は,別途 ssh-agent を起動する必要がある。 エージェントを利用する仮想端末をひとつ決めてそこで以下のように操作する。

ssh-agent $SHELL
ssh-add
emacs &

上記のように,編集作業などはこのシェルから起動したemacsを用いる。

リポジトリ管理下のファイル操作

リポジトリ管理しているファイルの,コピー/移動/削除は cp/mv/rm で行なってはいけない。リポジトリの登録情報とずれるからである。 これらは全てhgのサブコマンド cp/mv/rm で行なう。

hg cp 履歴を含めてファイルをコピーする。ディレクトリをまるごとコピーする こともできる。
hg mv 履歴ごとファイルを移動する,またはリネームする。 ディレクトリをまるごと移動することもできる。
hg rm リポジトリ内ファイルと作業ディレクトリのファイルを両方削除する。 ディレクトリごと削除することもできる。

任意時点のファイルの取り出し

hg log | less してみると,全ての履歴に 日付けとチェンジセットIDが付いているのが分かる。 このIDでリビジョン指定ができ,その時点の状態を特定できる。

たとえば,2013年2月2日の時点のファイルを取り出したいとする。 眺めたいだけ,1つだけファイルを取り出したい,全てのファイル集合を見たい, 場合によって切り替える。以下,REV というリビジョンの ファイルの取り出し方について示す。

あとから取り出すであろうリビジョンにはタグ をつけられる。

# 現在のワーキングリビジョンに hogehoge-1 というタグをつける
hg tag hogehoge-1

# リビジョン321に hogehoge-0.5 というタグをつける
hg tag -r 321 hogehoge-0.5

タグをつけたリビジョンは,タグ名でアクセスできる。たとえば,

hg cat -r hogehoge-0.5
hg archive -r hogehoge-0.5 /tmp/hogehoge-0.5

まとめ

コマンドライン操作

コマンドはたらき
hg statusリポジトリの状態表示
hg clone hg clone URL1 URL2
URL1 のリポジトリを URL2 に複製する。 URLにはリポジトリを示すディレクトリや,
ssh://サーバ/ディレクトリ
形式が指定可能。
hg pull 複製元リポジトリの更新を取得する。後ろにリポジトリURLを 指定して別のリポジトリからの取得も可能。
hg push 複製元リポジトリへ更新を送り込む。後ろにURLを指定可。
hg update リポジトリにある更新を作業ディレクトリに適用する。
hg merge 2つの更新をマージする。
hg resolve マージで生じた衝突を解決する。
-l 衝突ファイル一覧
-m ファイル 解決済ファイルをマーク
hg add 指定したファイルを新たにリポジトリに登録
hg log 更新履歴とログを出力
hg diff リポジトリ登録版との差分出力
hg diff -r REV [ファイル]
とすると,リビジョン REV との差分を出力する。 REV1:REV2 とすると,REV1 から REV2 への差分を出力する。
hg revert [file] 最後のコミット以後の修正を破棄して元に戻す。
hg revert -a で全てのファイルを戻す。

Emacsでのキー操作

ファイル編集モードでのキー

キーはたらき
C-x v vコミット
C-x v lログを見る
C-x v = 差分を見る
C-u C-x v = として,2つのリビジョンを指定すると それらの差分を見られる。
C-x v g hg annotate を行なう
どの行をどのリビジョンで採り入れたかを視覚的に調べられる
C-c h <hg pull を行なう
C-c h >hg push を行なう
C-c h c複数ファイルをまとめてコミット
C-c h shg status を行なう

log-view(C-x v l)モードのキー

キーはたらき
n次(下)のログへ
p前(上)のログへ
dそのチェンジセットの差分表示
aそのリビジョンで annotate 表示

vc-annotate(C-x v g)モードのキー

vc-annotate は,タイムマシンのように目の前に以前のリビジョンの 内容がどんどん現われるので,昔のリビジョンをじっくり参照したいときに便利。

キーはたらき
p古いリビジョンへ
n新しいリビジョンへ
jカーソル行のリビジョンへ
aカーソル行のリビジョンの直前リビジョンへ
wワーキング(作業中)リビジョンへ
v冗長表示切り替え
d該当リビジョンの差分表示
l該当リビジョンのログ表示
yuuji(atmark)e.koeki-u.ac.jp