matobaの備忘録

とあるPythonエンジニアのブログ。ソフトウェア開発、執筆活動、ライフログ。

バージョン管理システムの違いや仕組みについて調べていた話

数年前の過去の自分のブログ記事を整理していて、説明したい気持ちになったことがありました。*1

というわけでニーズがあるのか分かりませんが、今の時点での知識で説明を書きます。間違えてたり、変な話だったらすいません。

それは、バージョン管理システムの違いや仕組みの話です。

過去の自分が気になっていたこと

過去の自分はこんなことをしていました。

  • バージョン管理システムの違いが気になって調べていた
  • バージョン管理システム間で考え方の違いに戸惑っていた
  • 制作中の音楽のバージョン管理をしたくてモゴモゴしていた

今回は同じようなことを調べているかもしれない人の役に立てば良いなと思います。

ちなみに、ここに書いてある話は、コードだけバージョン管理できたらハッピーになれる人には関係のない話です。コードだけバージョン管理したい人は、GitとGitHubを使えば幸せになれます。

最後に、僕がなぜ、上記のようなことを調べた背景を書いてるので、気になった方は読んでもらえるとありがたいです。

まず、バージョン管理システムについて

とりあえず、こんなバージョン管理システムの話が含まれています。

  • Subversion
  • Mercurial
  • Git
  • 各種アプリ(エディタやDAW)に付属してるバージョン管理システム

僕が使ったことがあるバージョン管理システムの話を中心に広げます。

そもそもの話なのですが、バージョン管理システムで重要なのは、「バージョンとは何か」「バージョンをどうやって管理するか」です。

読者の方は、ソフトウェアエンジニアの方が多いと思うので、ソフトウェアのバージョン管理の話から考えます。

どうやってソフトウェアを管理するか

ソフトウェアのバージョン管理でいうと、一つのバージョンには、コードが記述された複数のファイルの状態とディレクトリ構造が含まれています。

で、どうやってバージョンを管理するか、の話なのですが、例えば、Mercurialはバージョンを直前のバージョンとの差分で管理しています。言い換えれば、データベースには直前のバージョンと直後のバージョンの差分が保存されています。Subversionもこの形式になっています。

各ファイルをdiffしてその差分を記録しているのです。

一方でGitはバージョンをファイルごと管理しています。 変更があったファイルをそのまま保存するのです。差分が一部だったとしてもファイルをそのまま保存します。ちょっとでも差分があれば、それは新しいファイルとして保存していて、差分がどれだけあるか、というのは保存していません。

この話の裏が欲しい人は、GitProのこの辺(Git - Gitの基本)を参照してください。

なぜ、Gitはファイルごと管理するのか

これがどう嬉しいかというと、Gitでは過去のバージョンにHEADを変えたときに、そのリビジョンにサクッと移動できるのです。Linuxでcdコマンドを実行した時と同じような体感で、リビジョンを変更できます。

直前との差分でバージョンを管理していると過去のバージョンを参照しようとしたときにも差分を辿っていかないといけません。複数のブランチで開発していたり、過去の履歴になればなるほど、過去バージョンの参照に時間がかかります。ただし、トータルで見たときのリポジトリの容量は差分で管理した方が少なくなります。必要なリポジトリの容量は、システムの費用に直結します。

これを別の言い方にすれば、過去のバージョンの仕様をコードやコミットログから把握しない前提を作れば、差分管理のバージョン管理を採用する方がトータルコストが下がる、ということです。逆に、差分管理のバージョン管理(SubversionやMercurial)を採用している場合、コードから仕様を把握するためのハードルがGitを使っている時と比べて上がっています。

GitはLinuxカーネルの開発者であるリーナストーバルス氏が、Linuxカーネルを開発するために作ったものです。Gitの略史というものがあるので、裏としてはこちらを参照してください。 see Git - Git略史

Gitは開発者がスムーズに特定のリビジョンを参照できるように作られた。ということを考えたら、どうしてそういう違いが生まれているのかを理解しやすいように思います。

どう使い分けるのですか?

こういう話をしていると、「では、若者(というか、数年前の僕)はSubversionやMercurialはどういうタイミングで使うといいのですか?」とか考えたくなっていました。

僕は今のところ、「とりあえず、Gitを使うようにしてGitを使ったときに発生する問題は、別の方法で対策する」と言うのが時代の流れのように思ってます。

とはいえ、気になる人は気になると思うので、今の僕が考えていることを説明してみます。

Subversionについて

あくまで予想の範疇ですが、Subversionが登場した時代(2000年ごろ)は、「今よりも業界の規模や予算が小さくて、費用を絞らざるを得なかった」とか「エンジニア一人当たりの生産性を上げるより、エンジニアの数を増やして、トータルの生産性を上げようとしていた」と言うのもあるのかもなあと、推察しています。

それから、その前段に「商用バージョン管理システムしかなくて、とりあえずオープンなバージョン管理システムが欲しい」みたいな話があったのかもしれないです。

ちなみに、過去の話がどうしても気になる人は、次のあたりの本を読むと良いかもです。

今は、「エンジニアの数を増やしても、トータルの生産性の上昇は効率が悪いから、できるだけ一人当たりのパフォーマンスをあげたい」とか、「ITがビジネスの中心になっているので、エンジニアの数を一人でも増やして、スピードアップしたい。エンジニアが高給でも雇いたい。開発マシンなんて安い」と言う時代なので、考えなくて良いと思います。

Mercurialについて

あと、Mercurialの歴史については、僕もWikipedia情報ですが、こちらもそもそもがLinuxカーネルの開発者であるMatt Mackall氏が開発を進めていたもので、「ファイルそのものを管理するのではなく、差分を管理すべき」と言うのがコアの主張だったようです。

see: Mercurial - Wikipedia

GitもMercurialも2005年に登場したものです。

これも、あくまで予想の範疇ですが、当時は、今よりもネットワークの速度が遅かったり、ストレージ単価が高かったりしました。あと、GitHubもなくて、オープンソース活動が活発と言う状況でもなかったのだろうなあ、と言うことを省みると「Gitのようなファイル単位で管理する仕組みを導入すると、開発を行うために必要なハードウェアが高価になる。そうすると、オープンソース活動への参加障壁が上がってしまう。Linuxのようなオープンに開発を進めるソフトウェアは、率先して、開発への参加障壁も下げるべきである。だから差分でファイルを管理すべきだ」と言う考えがあったんじゃないかなあ、などと思ったりしました。

ですので、もし、現場でMercurialを使おうとしてたり、使っている職場があるなら、そのベースには「たくさんの人にソフトウェア開発に参加して欲しい」とか「ソフトウェア開発をするの準備費用を下げて、できるだけ多くの人に給料を出したい」みたいな気持ちがある可能性もなきにしもあらずかなあ、と思います。

とはいえ、今はネットワーク速度も上がって、ストレージ単価も下がっていていますし、GitHubもありますので、そう言う話はおいといて、Gitを選ぶと言うことで良いように思います。

ついでのお話

あと、ついでに伝えておくと、少ないないとは思いますが、現在でもMercurialやSubversionを使っているプロジェクトはあるんじゃないかなあ、と思います。

そう言うプロジェクトに遭遇した際に「えっ、Gitを使ってないとかどれだけ時代遅れw」みたいな煽りを入れるのはやめましょう。

MercurialにしてもSubversionにしても、なんらかの問題を解決してきた素晴らしいソフトウェアですし、それらの技術を選定した人にも何らかの考えがあって選んでいるので、敬意を払うのをオススメします。

あと、そもそもソフトウェアが微妙なら途中で捨てられるので、長く続いているプロジェクトはそれだけで価値を生み出しているとも言えます。

と言うわけで、そういう現場に遭遇したら、先人に敬意を払いつつ「おっ、今、流行のDXに最適な題材があるじゃん」と思って、Gitへの移行を頑張るのをお勧めします。

なお、及川卓也氏のソフトウェア・ファーストによるとDXは、「自社プロダクトの成長に関わるコア技術を自分たちが主導権を持って企画・開発できるようにすること」です。Gitは管理より企画や開発のスピードに重きが置かれたツールですので、DXと言う言葉を使えば、Gitに移行しやすいかもしれません。(他人事ですが)

もし、それが嫌でさっさと最新技術だけを使いたいのであれば、速やかにスタートアップに転職するのがお勧めです。ただ、そちらはそちらで最新技術を使い続けるフィールドなので、どちらが幸せかはわからないのですが。

アプリのバージョン管理について

さて、上記の話を踏まえた上で、アプリでのバージョン管理の話をします。

アプリのバージョン管理を考える場合は、何をファイルそのもので管理して、何を構造データとしてデータベースに保存するかを考えることが必要だと思います。

当たり前ですが、次のようなデータは、データベースに保存すると、扱いやすいと思います。

  • プロジェクト名
  • プロジェクトの設定
  • 起動バージョン
  • 導入されているプラグインの種類
  • プラグインの設定情報
  • プロジェクトの中に含まれるファイルのパス一覧

言わずもがなですが、データベースに保存しておくことで、検索がしやすくなりますし、一括更新もできるようになるからです。

あとは、これらの管理は、差分管理ではなくて、要素単位のスナップショットをデータベースに格納していくと検索も復旧も差分抽出もしやすくなるんじゃないかなと思います。diffの場合は比較したいリビジョンを両方持ってきて、diffをとれば良いので、シンプルですし、復旧もある地点以前の最新を持ってくればいいので。

PythonのDjangoのAdmin画面には、変更履歴管理機能がついていますが、参考になりそうな気がします。

それから次のようなデータは、データベースに格納するのはなくて、安価なストレージに配置するのが良さそうです。*2

  • 画像
  • 映像
  • その他ログや時系列データ

当然ですが、一つ一つの情報が大きい上に、人間の視点でちょっと修正するとファイル全体に修正がかかります。一方でスナップショットをとらないと、いざ、特定のバージョンに回帰したいとなったときに、時間がかかりすぎます。そもそも、これらの情報のバージョン管理では、差分が求められることはそうそうないように思います。データだけを比較してdiffをとることは難しいので、基本的に二つのバージョンを出して、人間が見比べる状況になります。

ですので、基本は、常に新しいファイルをストレージに保存することにして、データベースで参照先を管理するのが良いように思います。また、上記のようなメディアデータの保存は、少々重くてUX的によろしくないので、バックグラウンドで常に最新を保存し続けて、ユーザーが保存ボタンを押したら、DBの参照先だけを変えている様子のアプリも散見します。

なお、常に新しいファイルをストレージに追加し続けるパターンだと、ストレージの容量が膨張し続けて辛くなります。それを抑止するための仕組みはいくつかあって、パッと思いつく限り、次のパターンがあります。

  • 過去のリビジョン数に制限をかける(10個しか保持しない)
  • 参照可能なリビジョンの期間に制限をかける(1年分しか見せない)
  • リビジョンを何らかの条件で圧縮する(翌日になると、前日のファイルは一つだけにする。指定した区間のリビジョンをまとめさせる)

ファイルのバージョン管理は、いわばバックアップなので、バックアップをどう言う単位で持っておくか、と言う話と同じ話になります。ついでに言うと、このバージョン管理は、課金ポイントになりやすいように思います。

バージョン管理は、データの保持の仕方によって、使い勝手がかなり変わると思いますので、データをどうもつか、というのが結構重要なポイントだと思います。

そもそも、僕がやりたかったこと

はい。とりあえず今、思い出し分は書いたので、ここからはおまけの僕の話です。

僕は、バージョン管理について、いろいろ気になって調べていたのですが、その背景には「DAW(デジタルオーディオワークステーション)で音楽を作っている時のプロジェクトをスムーズにバージョン管理する仕組みが欲しい」と言う気持ちがありました。

学生時代の私は、DAWを使って音楽を制作していたんですよね。その当時で、50トラック以上のwavファイルをDAWに展開して、制作を進めていました。

waveファイルは、自身が演奏して収録したものから、打ち込みしたmidiデータをレンダリングしたもの、共同制作者から渡されたものもありました。そして、音楽の制作は、自分のものだけでなく、人から依頼された物もあり、クライアントから渡された音素材もあります。

依頼されて制作している場合は、複数バージョンを作った後に、レビューに出して、レビューの後に任意のバージョンから制作を再開する必要が出てきたりします。ここでバージョン管理という概念が必要になりました。

ただ、当時はバージョン管理の機能がなかったので、僕はプロジェクトを丸ごとコピーして、ファイル名でプロジェクトバージョン管理したりしていました。ただ、これの管理がなかなか辛い。特に、途中から編集したりすると、結局どれが最新か分からなくなったりするわけです。辛い。

この問題を僕は解決したかったりしたんですよね。そして時代は流れて、現在では、僕が使っているStudio Oneと言うDAWにはバージョン管理機能がついていて僕はこちらを使っているので、僕の問題は解消されました。

www.mi7.co.jp

ただ、せっかく集めた知識なので、誰かが再利用してくれたらいいな、と言う気持ちを込めて、ここに書きました。

ではでは

*1:ちなみに「この情報は害悪だな」とか「この主張は恥ずかしいな」と思った記事を整理するのが目的なので、その記事は、すでに非公開にしています

*2:映像データは、もうちょっと工夫が必要かと思いますが、本記事では、詳しく話しません

ソフトウェア開発の観戦とエッセイ

ちょっと古い本だけど、面白い本を見つけたので、書いておきます。

見つけた本はこちら。

BEST SOFTWARE WRITING

BEST SOFTWARE WRITING

  • 発売日: 2008/02/21
  • メディア: 単行本(ソフトカバー)

何が面白いか、を一言でいうと、普遍的な息の長いテーマを扱ってるところです。日本語版は2008年に出版されているけど、僕は今でも読む意味があると思っています。

いや、どうかな。とりあえず、僕は読んでいて楽しいと感じていて、ソフトウェアに関するエッセイが好きな人は読むと楽しめるように思います。と言うわけで読みながら読んでいる話を備忘録に書いています。

この本を読んだからと言って、何か新しいテクノロジーが扱えるようになるわけじゃないし、給料があがるわけでもないと思います。

なんというか、小説を読んだから何かができるようになるわけではない、という話に近い。ただ、この本を読むとソフトウェア開発者としての生活が、少し充実するかもしれません。

じゃあどんなことが書いてあるのか、という話になりそうなので、文章を一部引用して書いておきます。

プログラミングの世界では、毎日気付かれることなく戦争が行われている。それは人間とコンピュータサイエンティストの間の戦いだ。それはシンプルでルーズでしなやかで人間的なコードの書き方を望む人と、クリーンで明瞭ではっきりしていて正確なやり方でコードを書くことを望む人の戦いだ。それはPHPとC++/Javaの間の戦いだ。そしてかつてはCとdBASEの間の戦いだった。

この文章はP25に書いてあります。

今はPHPとC++/Javaの間の戦いではないと思いますが、近い対立はあるように思います。

その構図や議論を少し離れて見ながら、どうなるのかを考えながら見たり何かを応援するのは、楽しさがあります。

それは、おそらく、野球ファンがプロ野球を観戦する楽しみ方に近い気がします。

僕は、自分が使っている技術やシステム、サービスの開発背景や経緯、その途中の意思決定などなどの記録を読んで、ふむふむ、とするのは、割と好きな方ではありますが、その活動の亜種ですかね。

ソフトウェアやシステムは有機的にできていますが、その構成要素や仕組みを知って、ナルホド、という気持ちなる遊びです。

pytestで一時的に失敗を許可する

誰かのための備忘録です。あと、日本語の技術記事を増やそうかな、とも思っています。

pytestでテストを書きつつ、コードを修正していた際、既存のテストが失敗することはあります。

で、たくさんテストが失敗しすぎて、結果が見にくいと感じることもあります。

そういうときに、一時的にあるテストケースのテストの失敗を許容する書き方があります。

import pytest

@pytest.mark.xfail
def test_func():
    assert False

こんな感じです。 @pytest.mark.xfail を書くと、「このテストは失敗する」ということをマークすることができます。

詳細は、ドキュメントをどうぞ。

docs.pytest.org

他にも pytest.mark.skip とか pytest.mark.skipif とかもあります。

例えば、先日紹介したpytest-watchでテスト回しているときに、リファクタリングの修正を入れたらテストが大量にこけるようになったて、テスト一つ一つに対して、修正を検討しないといけないんだけど、まとめて検討しにくいときに使ってたりします。

複数の問題が絡まっているときも、問題を一つずつ解消していきたいので、原因を確かめて、原因を認識したテストケースは修正を後回しにするために、xfailをつけたりしてます。

pytestでクラスやメソッド指定でテストを実行することもできますが、できればpytest-watchである程度の範囲をまとめてテストしながら修正していきたいので、そんな進め方をしてたりします。

ファイル更新時にpytestを自動実行する

誰かのための備忘録です。

Pythonでテストを書くときに、pytestを使うことはよくあります。

で、ファイルを更新した後にpytestでテストを実行したいこともよくあると思います。

そういうとき、ファイル更新を検知して、自動でpytestを実行してくれるツールがあります。

pypi.org

使い方は簡単です。

pytestを実行している開発環境で、pip install してptwというコマンドを実行するだけです。

$ pip install pytest-watch
$ ptw

ptwを実行するとファイルの監視が始まります。

僕は、コードを書く際、技術的な課題が概ね解決できそうなイメージができたら「とりあえず、一気通貫の入出力だけ確認するユニットテストを書く。その後は、pytestを自動実行しながら良い感じにリファクタリングしつつ、細部のテストを追加していく」という進め方をすることがあります。

そういうとき、pytest-watchを起動しておくと、便利です。「さっきまで動いていたけど、いつの間にか動かなくなった」という問題を避けやすくなります。

dockerで開発している人は、以下の話が参考になるかもです。

https://github.com/joeyespo/pytest-watch/issues/91

と言いつつ、僕は、以下のページで紹介されているwatchdogを使いつつ、自分でdockerのテストコマンドを実行してたりします。

qiita.com

FeedlyというRSSリーダーを改めて使い始めた備忘録

Feedlyを入れた。

Feedlyは、RSSリーダーの一種。

今更、RSSリーダー?と思う人もいるのかもしれないけど、改めて戻ってきた。

feedly.com

理由

Twitterが自分の中で、うまく機能しなくなった

なんとなく最近、Twitterを眺めていると、同じようなツイートばかりが流れてくるような気がしている。

自分がフォローしている人が流したニュースではなく、誰かと誰かの議論が流れてくる。

僕が見たいのはそれではない感じだった。

ニュース記事が流れてくる場所を作りたかった。

新しい情報が流れてくる場所が欲しい

繰り返しになるけど、新しい情報、ニュース記事とか各社のブログとか、がいい感じに流れてくる場所が欲しかった。

新しい情報が流れる場所をフォローしておかないと、だんだんよくわからないことが増えていくな、という気持ちがある。

とは言えて、最近、よくわからないと思うことが多すぎるとも思うので、情報を過剰摂取しすぎないように注意してはいるのだけど。

いろんなブログ記事の更新をフォローしておきたい気持ちがあって、それをやるならRSSなのかな、と思って、RSSリーダーを入れた。

あんまりRSSリーダーを知らない

実はRSSリーダーをそんなに調べていない。

とりあえず、次の条件を満たすものを探したらFeedlyにたどり着いた。

  • Webブラウザで管理できる
  • スマホでも見れる
  • Firefoxに拡張がある

余談

他の人はどんなふうにして、情報を収集しているのだろう、と思っているけど、なかなか話をする機会がない。

Gitで変更したファイルを変更前に戻す

Gitで変更したファイルを変更前に戻すコマンドのメモ。

以下のようにrestoreを使う。

git restore <filename>

とりあえず git stash とかで変更を一時待避したり、 git checkout -- <filename> していることもあるのだけど、別に待避する必要がない時はあるし、restoreの方が明示的だと思うのでこっちを使っていきたい。

ちなみに、コミットする前のファイルの話なので、コミットしている場合は、 revert や push前なら reset --hard を検討することになるのかと思います。

忘れるために記録を書く。忘れたときのために記録を書く。

Twitterで以下のような話をした。

この話をした後にこんなことを思った。

何となく自分が記録という概念についてわかっていないこと、もしくは忘れているようなことがあるように感じた。

何となく最近の自分は、ブログを書くモチベーションが湧かなくなっていることが気になっていた。あと、voluntas さんのブログ(主に時雨堂関係の情報)を拝見した際に「この記事は、どういうモチベーションとか、どういうタイミングで書こうと考えたのだろう?」と思ったことがあった。(対象読者や主旨が想像できないけど、重要な情報が書かれていると感じることが多いので)

で、もう少し考えていると、自分の中で「記録」という概念の意味がぼんやりしていることに気づいた。

そこで、「記録とは何か」を辞書で調べていると次のページにたどり着いた。

kotobank.jp

気になったのは以下の点。

なお近代では,記録と文書の概念上の混用がみられ,文書課の保存資料を記録と呼ぶこともある。

後、次の話も気になる。

著作物である典籍や,おのれの意思・用件などを相手に伝える目的で書かれたものを文書(もんじよ)とよぶのに対し,原則として自己(近親者あるいは所属の機関なども含む)の備忘のため書きとめたものを記録といい,主として日記類がこれに該当する。

だんだんわかってきたのが、自分の中で「文書」と「記録」の境界がボケているということ。

以前は、文章を書くと言えば、ブログに書くことが多かった。そしてブログに書くことは、備忘録だった。自分が調べたことや学んだことをメモする場所がブログだった。

ただ、次第に自分の文章が誰かに読まれることを意識するようになると、記録というより文書の側面が強くなっていった。ブログの記事もある程度は、想定読者を考えつつ、文章や情報を整えることが増えた。要は、ブログの記事が文書化していった。

すると、だんだんブログが書きにくくなっていった。そもそも誰かに何かを伝えたい時にブログが最も適切な手段になることはそう多くないと思う。不特定多数が見る可能性のある場所に想定読者を設定して書くのは、なんか違うような気がしてしまう。

そういう話をいろいろ考えて、まとめると、自分が忘れるための記録を残すために書いてないからブログを書きにくいと感じるんだなあ、と思い始めた。また、自分以外の誰かが忘れた時や知りたいと思った時に参照するために記録を残していると考えると、ブログを書く理由が自分の中で腹落ちした。

というわけで、しばらくはブログの更新がなかったけど、改めてブログを更新していきたいと思ったところでした。

追伸:ついでに、ブログのタイトルは「matobaの備忘録」に変えました。