Git/Mercurial/分散バージョン管理システムの基礎

Gitに関する話をするとき、「ん???」となることがまぁまぁある。

で、多くの場合よく考えれば「GitHubの概念に引きずられている」ものが多いように感じる。

今回は、Git、そしてMercurialを含めて分散バージョン管理システムに関する概念と用語を、簡潔・明瞭に説明したいと思う。 なお、Bazzrその他に関しては私は使ったことがないので、分散バージョン管理システムの説明といいながら、GitとMercurialだけで進めさせていただきたい。

概念に関するもの

リポジトリ

恐らく、用語としてはこれが最も難しい。

「リポジトリとは歴史である」などといったりするのだが、どうも各々の定義にぶれがある。

であるから、Gitの場合は、.gitディレクトリ、あるいは*.gitディレクトリ(ベアリポジトリ)のことを指していると思えば良い。 また、.gitがあるディレクトリは「ワーキングツリー」である。

Mercurialの場合は.hgディレクトリを指す。

これは単にファイルであるだけではなく、ファイルの変更などを管理するための情報をもち、実際に管理することができる。

ローカルリポジトリ

ローカルリポジトリは、ワーキングツリーから見て、そのワーキングツリーが所属するリポジトリ(つまりは、ワーキングツリー先頭の.gitあるいは.hg)を指す。

ローカルリポジトリという語が出てくるのはリモートリポジトリに対する対比である。 なぜならば、リポジトリの操作はローカルリポジトリ(ワーキングツリー)上で行うため、「手元側」を意味することになるからだ。

リモートリポジトリ

リモートリポジトリは、ワーキングツリー、あるいはリポジトリから見て、自身ではないリポジトリを指す。

リモートリポジトリは一般的にはローカルリポジトリに対して何らかの関係性を持つ。ただし、持たない場合もある。 何らかの関係性とは、ローカルリポジトリがリモートリポジトリのことを登録しているか、リモートリポジトリがローカルリポジトリのことを登録しているかを指す。

「リモート」といっても、あくまでも「このリポジトリの外」の意味であり、ネットワーク越しであることを意味するわけではない。 むしろ、最も基本的なGitやMercurialの運用においてはリモートリポジトリはファイルシステム上にあるほうが普通であり、ネットワークにおけるリモートを指してはいない。

また、場合によってはそもそもローカルリポジトリ上で登録されたリポジトリのことだけを指してリモートリポジトリと呼ぶ場合もある。

コミット

リポジトリによって管理されるファイルのある状態の記録である。 GitやMercurialの変更は連続的に記録されるわけではなく、コミットした瞬間ごとが記録される。

コミットは本質的にリポジトリへの書き込みである。 このことから、最終的な修正が反映される権威リポジトリが存在する場合、その権威リポジトリに対するpush、あるいは権威リポジトリ上でのコミットを「コミット」と呼ぶ場合がある。 この場合、「そのリポジトリを更新する行為」を指すのであり、その行為をしうる者を「コミッタ」と呼ぶ。

ステージング

Mercurialにはない、「コミット候補」。

基本的にはGitの場合、ステージされたものはステージされた状態で保たれ、コミットされる。 ステージされてからコミットされるまでに変更は加えられないので、「コミットする前に考える」段階があると考えて良い。

実際のところ、ほとんどの場合ステージングは省略されている。

HEADはGit独自の概念である。

HEADは コミットを指している訳ではない 。 HEADはあくまで位置である。

リポジトリがリモートリポジトリと同期される関係にある場合、リモートリポジトリと同期した位置というものが記録されている。 HEADは全体で一番最後にコミットされた位置である。

コミットを取り込む場合、取り込む側(つまり、それによって変更される側)のほうがHEADの位置が前にあってはいけない。

フォーク

フォークは分散バージョン管理システムにおける用語ではない。

フォークは(由来は置いておくとして)ソフトウェアを複製し、複製元とは異なる未来を歩むことを意味する。

分散バージョン管理システムにおいては、行為としてはcloneすることがまさにforkすることを指す。 ただし、cloneした後に異なる未来を歩み、それを元のリポジトリに反映する場合や、恒常的に元のリポジトリの変更を反映する場合はforkしたとは言えない。

先割れしたフォークの先端が交わることはない。forkは決別なのである。

ブランチ

ブランチの概念はソフトウェアによって随分違う。

Gitの場合はあくまで歴史の分岐である。 ブランチを作ることでブランチ作成の起点になるコミットから、他のブランチに影響されることなくコミットを作っていける。

Mercurialの場合は、ブランチは位置情報になっている。 枝分かれしているというよりは、同じように時間が流れる平行世界みたいな状態である。

両者の大きな違いとして、Gitはブランチを作ったらそのまま完全に違う未来を歩んでもいいので、最初のブランチであるmasterブランチにそこまで特別な意味がない。 対してMercurialの場合は一種のコミットのような扱いになり、ブランチは最終的には取り込まれるか、クローズして捨てられるかすることを想定している。 だから、Mercurialの場合はdefaultブランチが本命である。

なお、Gitのmasterブランチは基本的に進んだHEADを持っているので、masterブランチをリリースブランチにするのはちょっとまずい。 リリースブランチは別に切るべきだ。 対して、Mercurialは一番進んだコミットを持つdefaultに合流するようになっており、あんまりリリースのことは考えてない感じになっている。

また、ブランチの大きな違いとして、Gitはブランチは削除できるが、Mercurialは閉鎖できるだけで削除はできない。 どうしても削除したい場合は方法がなくもないが、それはそれでMercurialでは本来禁止されている歴史操作を使ってそのブランチの世界線にあるコミットを全て消滅させるというすごいことをすることになる。

さらにもうひとつ大きな違いとして、Gitの場合ブランチは個々のリポジトリに属している。明示して送りつけない限りはpushあるいはpullするのはブランチ単位である。 対してMercurialは全てのブランチが共有される。だから、Mercurialでのpushあるいはpullするのはリポジトリ全体である。

競合 (conflict)

バージョン管理システムにおいて最も重要なのは、「同じファイルを同時に変更することに対して保護する」である。 古代のバージョン管理システムであるRCSでは、「変更可能な状態で持ち出せるのは1ユーザーだけ」という方法で管理していた。

GitもMercurialも、基本的には同一ファイルに対する変更を競合とみなす。

ただし、Gitの場合は変更点が重複していなければ競合にはならない。 Mercurialの場合は変更点が重複していなくても同一ファイルに対して変更していれば競合になる。

ただし、Mercurialの競合はそもそも歴史が割り込まれた時点で発生するため、こっちも向こうもそれぞれにコミットしたんだよね、という状態になったら確実に競合が発生する。 この変更が統合可能なのであれば、mergingという扱いにはなるものの、実際にはmergeは必要なく、単に「歴史を統合したコミット」を作れば良いようになっている。

ここらへんはGitのほうがきっちりしていて、Mercurialの場合はそれぞれが無軌道に変更を加えているとえらいことになる。 Gitではそもそもpush可能なのはベアリポジトリだけなのに対し、Mercurialではベアリポジトリという概念がなく、リポジトリは須らくワーキングツリーを持っているという考え方になっている。 でも、複数人で作業するような場合はワーキングツリーに対する変更を加えない、つまり自分でコミットを作成しないリポジトリを作ってそこにpushするようにしておかないと混乱を招くことになる。

pull request

pull request (通称プルリク)は、GitでもMercurialでもなく、 GitHubの機能である。 ちなみに、GitLabでは “Merge Request”という名前で同種の機能がある。

リポジトリに対してpushするためには、当該リポジトリに対する書き込み権限が必要である。 読み取り権限があればcloneできるため、cloneされるリポジトリは所有者が異なる可能性があり、元のリポジトリに対する書き込み権限がないことも少なくはない。

もちろん、書き込み権限があるのであれば当該リポジトリに対してpushすれば良いのだが、ない場合は当該リポジトリの書き込み権限を持つ者にpullしてもらうことになる。 しかし、その場合「pullしてほしい」と伝えなくてはならない。これを、「pullして欲しいと伝えて、変更点を明確にして、ついでにボタン一発でpullできるようにしたもの」がPull Requestである。

これに関しては誤解が深く、GitHubでGitを触り始めた人がだいたい混乱している。

アクション

init

リポジトリを作成すること、だが、どちらかといえば「今いるこの場所をリポジトリにする」のほうが実態を指している。

ただし、Gitにおいてはgit init --bareがあるためそうとも限らない。 この場合はベアリポジトリを単純に作成する。

このアクションはローカルリポジトリが存在しない状態で行う。

clone

リモートリポジトリの複製を作成する。

このアクションはローカルリポジトリが存在しない状態で行う。

push

ローカルリポジトリのコミットをリモートリポジトリに書き込む。 リモートリポジトリの書き込み権限が必要である。

pull

リモートリポジトリのコミットをローカルリポジトリに書き込む。 リモートリポジトリの読み取り権限が必要である。

add

基本的にはワーキングツリー以下のファイルをリポジトリの管理下に加えるアクション。

Gitの場合はステージングの際にも使用する。

Mercurialの場合、ワーキングツリー以下で明に除外されていないのに管理外にファイルがあることは望ましい状態ではないと考えるため、addの手順はまぁまぁ省略される。 Gitでは省略はできない。

merge

Gitにおいては異なるブランチを取り込むこと。

Mercurialにおいては、割り込みの発生した歴史を一本にまとめたコミットを作ること。

reset / rollback

resetはGitにおけるアクションで、ステージされたファイル、あるいは最新のコミットを取り消す。

Mercurialでは最新のコミットを取り消すrollbackがあり、Mercurialではコミットの歴史を操作するアクションはこれが唯一。 ステージして慎重にコミットするGitと違い、Mercurialは一発でコミットをキメてしまうため、rollbackは結構よく使うし、実際に簡単に使えるようになっている。

revert / backout

revertはGitとMercurialで全く意味が違う。

Gitにおいてはコミットを取り消す。この場合、そのコミットにおいて行われた変更そのものを元に戻す。 Mercurialは歴史を変更することはできないので、あるコミットで行われた変更を元の状態に戻す変更を加えたというコミットを作成する。それ用にbackoutというアクションがある。

Mercurialのrevertはワーキングツリーのファイルをコミットの状態に戻すことを指す。 これはGitであればgit checkout <commit> <file>に相当する操作である。 Gitのcheckoutはこれとは全く異なる「ブランチの切り替え」という機能も兼ねており、少々わかりにくい。

Gitには他にも歴史操作に関するアクションがあり、特にrebaseはまさに歴史修正主義者のためのコマンドである。

あんまり知られていないが、Gitにはblameという大変便利な歴史チェックコマンドがあったりする。 そして、実はMercurialにも似た感じのことができるannotateというコマンドがあり、hg annotate --user --numberとやればblame相当になる。

「revertするぞ」と言われたら、「お前のコミットは問題があるからなかったことにする」という意味になる。 例え実際にはMercurialを使っている場合でも「backoutするぞ」じゃなく「revertするぞ」と言う場合が多い。

diff

何か(コミット, タグ, ブックマーク, e.t.c.)の間でファイルの変更を比較するアクションである。

実はGitのdiffはGitリポジトリ外でも使うことができる。

diff -uと同じだろ?何が嬉しいんだよ」と思うかもしれないが、実はGit diffはインラインで変更を表示することができるのだ。 これがすごく便利。

stash

ワーキングツリーに対する変更を保留にするGitのアクション。Mercurialには全く存在しない。 ほとんどの場合、「作業すべきブランチを間違えた」という場合に別のブランチに変更を持っていくために行う。

すごく便利である。 そもそも、ブランチに関する操作はMercurialよりGitのほうがずっとやりやすい。

Stellaの言語デザイン

Stellaの話、第三段。 今回は、特徴的な「Stella logicとDSL」の話をしていく。

正直なところ、Stella logicは仕方ないとはいえあまりスマートではない設計がなされている。 なおかつYAML形式だ。 確かに知識が少なくても書けるようには工夫されているが、その一方で労力、時間、そしてミス発生の余地という犠牲を払うことになる。

だが、このYAML形式、というのがポイントになっている。 その不自由は、与えられたものを使うだけの人にとってのみのものだからだ。

Stella logicの構造

Stella logicの肝となるのはcontext leafと呼ばれるものだが、これは

[ method, arg ]

という構造をしている。 methodはtests及びactionsが存在し、それぞれ「テスト名」「アクション名」と呼ばれている。 argの型はmethodによって決まっており、

[ method, arg1, arg2...]

のようにはならず、常に単一の値である。

例えば

[ match, String ]

のように定義されており、ものによっては

[ msg, (String|Array)]

のように定義されている。

もうなんとなくわかるかもしれないが、思いっきりLispである。 もっとも、Stella logicはなんとなくLispっぽいだけで純粋にLispスタイルなわけではない。

基本的にはcontext leafまでは次のように定義されている。

root.is_a? Hash
root.has_key? "Context"
root["Context"].is_a? Array
root["Context"].all? do |context_tree|
  context_tree.all? do |context_branch| 
    context_branch.is_a? Hash && context_branch.has_key? "tests" && context_branch.has_key? "actions" and
    context_branch["tests"].is_a? Array and
    context_branch["actions"].is_a? Array
  end
end

もっと具体的には次のようになる。

root = {"Context" = {"main" => Array.new} }
root["Context"]["main"].push({"tests" => Array.new, "actions" => Array.new})

有効なcontext leafまで書くと

root = {"Context" = {"main" => Array.new} }
root["Context"]["main"].push({"tests" => [ ["match", "Hello"] ], "actions" => [ ["msg", ["Hello, ", "world!"] ] ]})

みたいな感じ。

Lispで書くと

'(
  ("Context" (
    ("main" (
      ("tests" (
        ("match" "Hello")
      ))
      ("actions" (
        ("msg" ("Hello, " "world!"))
      ))
    ))
  ))
)

である。そして、YAMLにすると

Context:
  main:
    -
      tests:
        - [match, Hello]
      actions:
        - [msg, ["Hello,", "world!"]]

となる。 類似の感覚でRubyで書くと

{
  "Context" => {
    "main" => [
      {
        "tests" => [
          ["match", "Hello"]
        ],
        "actions" => [
          ["msg", ["Hello, ", "world!"]]
        ]
      }
    ]
  }
}

さらにJSONで書くと

{"Context":{"main":[{"tests":[["match","Hello"]],"actions":[["msg",["Hello, ","world!"]]]}]}}

この中ではRubyで書くのが,を忘れそうで一番危ない。 ちなみに、これに関してはPerlで書いてもRubyと全く同じになる。

入れ子が深く複雑に見えるが、作業自体は骨格ができてしまえばコピペ+改変でがんばれるので難易度自体は低い。 これを可能にするためコピペベースとなるサンプルも提供されている。

「YAMLで書く」である合理性

見ての通り、Stella Logicは連想配列, 配列, 文字列の組み合わせによって表現できる。 これらは多くのプログラミング言語に備わる基本的データ型である。

そして、そのような基本的データ型であるからこそYAMLやJSONで表すことができる。

YAMLは汎用性のあるデータフォーマットだ。

以上を以て「Stella logicは任意の言語で記述できる」が成立する。 例えばRubyなら

require 'YAML'

root = { "Context" => { "main" => [
  {
    "tests" => [
      ["match", "Hello"]
    ],
    "actions" => [
      ["msg", ["Hello, ", "world!"]]
    ]
  }
]}}

YAML.dump root, STDOUT

で良いわけだ。

専用の記述法を提供すればもっと簡単に書けるようになるが、そうなるとその記述法以外は許されなくなってしまう。 「このようにYAMLで書いてください」ということをそのまま受け取るだけであれば、もっと良い記述法が提供されるべきと思ってしまうだろうが、知識と発想さえあれば汎用性があり容易な手段で提供されることは良いことだと判断できるだろう。

ちなみに、JSONでなくYAMLである理由は、「JSONだと閉じ括弧のあとのカンマを忘れるから」である。 プログラマですら忘れるものを一般の人が意識できるわけもない。

なお、YAMLでの出力は難しい言語処理系を使う場合、JSONで出力しておき、

$ ruby -ryaml -rjson -e 'YAML.dump JSON.load(ARGF), STDOUT' logic.json

なんてワンライナーで変換できる。

だからDSL

とはいえ、YAMLで書くのは結構かったるい。 基本的なスタンスは「書きやすい方法は各々用意すべし」なのだが、それが難しく不自由を強いられるケースもあるだろう。

そのため、Stella logic builderというRubyライブラリを提供している。

このライブラリは、Stella logicを書きやすいようにするための語彙を提供する。 例えばコンテキストリーフノードmsgについて

msg("Hello, ", "world!")

のように書けるようにする。これを使うだけで前述のコードが

{ "Context" => { "main" => [
  {
    "tests" => [
      match("Hello")
    ],
    "actions" => [
      msg("Hello, ", "world!")
    ]
  }
]}}

とちょっと読みやすくなる。 もちろん、こんな使い方をするためでなく、Rubyで独自のより書きやすい書き方を制作する際の補助である。

同ライブラリには私なりの書きやすい書き方も用意されている。

c = Stella::Builder.new
c.cxt("main")
c.push(
  "tests" => [match("Hello")]
  "actions" => [msg("Hello, ", "world!")]
)
c.out

Stella::Builder#cxtはデフォルトが"main"なので別に呼ばなくて良い。

Stella::Builder#outは出力用メソッドなので、前述と同じ内容の記述であれば必要ない。 つまり、

c = Stella::Builder.new

c.push(
  "tests" => [match("Hello")]
  "actions" => [msg("Hello, ", "world!")]
)

である。すっきり。

これはRubyistにとって書きやすい設計を目指しているものであるから、「不自由を強いられる人にとって書きやすい書き方」ではない。 同ライブラリはそのような人に向けた書き方も提供している。それがStella DSLだ。

Stella DSLを用いて書くと次のようになる。

dsl

as "main" do
  on match("Hello")
  act msg("Hello," "world!")
end

finish

次のような書き方も可能。

dsl

as "main" do
  lets do
    match("Hello")
    msg("Hello, ", "world!")
  end
end

finish

あまり違いがないように見えるかもしれないが、コンテキストリーフノードが複数ある場合は大きく変わってくる。

跡形もないだろう? Stella LogicがYAMLであることが何かを縛り付けるためではなく、それぞれの技倆における自由を与えるものであることがおわかりいただけたかと思う。 もちろん、Sella DSLが正解なわけではなく、むしろ可能なのであれば最も書きやすい方法で書くことが推奨される。

Stella DSLはRuby内部DSLであるため、書き方の自由度は高く、表現上の自由度は大幅に上がっている。 Chienomiを読んでいる諸兄諸姉はプログラミングのできる人がかなり多かろうから、ここまで説明すれば十分に理解していただけていることと思う。

ちなみに、メタ記述法をとらないのであればmatch("Hello")と書く方法自体はとても簡単で

def match(str)
  ["match", str]
end

で良い。

Stella DSLはプログラムを自在に書けない人のためのStella logicの別記法であると同時に、「Stella logicはあなたの技術力によって生成するものである」という原則の純正サンプルである、ということだ。

専用記法とは

専用記法を採用するとしたら、多分こんな感じだろう。

@main

/test

match Hello

/act

msg "Hello, " world!

@でコンテキスト切り替え、/で定義する内容の宣言になっている(testで新規リーフになる)。 メソッドの引数はShell wordsとして解釈される。

他の記法と比べて文法が非常に寛容でエラーになる可能性が低い。 この記法自体は良いと考えていると、なんなら今からでも採用したいくらいだし、実際それは可能である。

だが、もしいまからこの記法を採用するとしたら、それはStella Logic(YAML)を生成するメタフォーマットになるだろう。 このルールがあるため、新たなる記法を追加するのは簡単だ。

それがもし、Stellaが受け付けるのがこのフォーマットだけだとしたらどうだろう? もちろん、それをプログラムによって生成することは不可能ではないが、そのジェネレータ、トランスレータは各々が実装しなくてはならない。 そして、これはジェネレータが生成しやすいフォーマットでもない。

YAMLという一般的なフォーマットをStellaが受け付けることによって扱いやすくなっていることが分かるだろう。

in the codes

Stella DSLは基本的に記法が異なるだけで、考え方は変わっていない。 ところが、最終的にYAMLを出力すればいいため、自由度はもっともっと高い。

例えば私が書いた「選択後特定のアクションを経て同一のコンテキストに合流する」というコードは

def sel(msgs: nil, opts: nil, params: nil)
  ccount = @ccount
  cpre = @cpre
  pname = @pname
  as("#{@cpre}#{@ccount}") {
    on finally
    
    actarg = []
    msgs.each {|i| actarg.push msg(i)} if msgs
    params.each {|i| actarg.push appendparam(pname, i)} if params

    ohash = {}
    opts[0].each_with_index {|x, i| ohash[x] = "#{cpre}#{ccount}-#{i + 1}" }
    actarg.push(sel(ohash))

    act *actarg
  }


  opts[1].each_with_index do |x, i|
    as "#{@cpre}#{@ccount}-#{i + 1}" do
      on finally
      act *x, pass("#{cpre}#{ccount + 1}")
    end
  end

  @ccount += 1
end

となっている。(Stella DSLを使っている)

余談だが、インスタンス変数をわざわざローカル変数にしているのは、StellaDSL#asObject#instance_evalを呼ぶのでインスタンス変数が読めなくなるためである。

Stella::BuilderクラスはStella::Builder#pushなどによって簡単にコンテキストリーフの追加が可能であるため、よりプログラム的に生成しやすい。

例えば実用的な意味があるかどうかは別として、学習を元に条件とアクションのペアを生成していき、トポロジカルソートによって構成する、という方法もありうるわけだ。

finally

“Stella logicはLispライクな考え方のYAMLである”

これによって得られたものは

  • 容易に変換でき、任意の言語で書くことができる
  • 任意の言語を用いて容易に異なる記法を実装することができる
  • ループや再利用、関数的コールなど本質的記述に含められない機能を生成に含めることで利用できる
  • 単純な記述でなくプログラムによって生成するといった応用が効きやすい

結局、どれほど気の利いたフォーマットや記法を提供することよりも、汎用性があり単純な形式を採用するほうがユーザーに利する。 そこまで考えて採用したわけではないのだが、そのことが改めて実感できるものであった。

Sharhiveって何か

Sharchiveの話をしたら、「それなに」という方がいたので、「あぁ、そうか。思いっきり昔話だもんなぁ」というわけでちょっとSharchiveの話をしよう。

概要

Sharchive (Shell Archive)はアーカイブファイルフォーマットである。filename suffixは.sharで、コマンド名もshar

アーカイブが7bit ASCIIで書かれたshスクリプトになることが特長。展開時はシェルスクリプトとして実行することで展開できる。

「危険ではないのか」という意見に関して答えると、非常に危険である。 純粋なシェルスクリプトであるため、Windowsの自己解凍形式なんかよりはるかに危険である。

背景

Sharは1995年頃まで使われていた。 tarがアーカイブファイルとしてはまだ一般的でない頃に使われていたのだが、アーカイブファイルフォーマットとして主流だったことはない。間違いなくやや特殊なアーカイブだった。

Unixプラットフォーム上ではshがあることは期待できたので、そうした互換性問題を回避するという面はあった。 だが、Sharが使われていた頃であってもtarがないということはそれほどなかった。

Sharが使われた最大の理由はネットニューズである。

ネットニューズを知らない人は多いだろうが、だいたいやりとりの感覚としてはメーリングリストに近い。 そして、当時はMIMEが制定される前で添付ファイルというものはなかった。つまり、データはメール内に書くしかなかった。 なおかつ、まだ8bit目がチェックディジットに使われている時であり、7bit ASCIIでしかメールが書けなかった。

もちろん、この頃でもtarでアーカイブを作り、uuencodeする、という選択肢はあった(Base64という選択肢はまだなかった)。 だが、これだとネットニューズで見ただけでは、それがどのようなソフトウェアなのかということを推定することができない。 Sharchiveであれば、特にスクリプトファイルやソースコードの場合、ネットニューズ上で確認するだけで内容を把握することができる。 メールと違い、uuencodeされたソースを展開するには取り込んでから保存してソースを取り出すという手間があるし、今と違ってX window system前提というわけでもなかったのでコピペが簡単にできたわけでもない。

こうしたことから使われていたのだが、バイナリサイズが大きくなってくると目視は困難だし、効率も悪く、なにより目視確認できないSharchiveは危険極まりない。 これによって、MIME制定以降Sharchiveは使われなくなっていき、やがて全く使われなくなった。

現在

Manjaroの場合sharutilsというパッケージがextraに存在する。

FreeBSD/DragonflyBSDのtarはlibarchiveを使っており、Sharに対応している。--formatオプションやaオプションで作成可能。

もちろん、展開はshでできる。そのため、展開そのものは今も苦労なくできる。

もはや使っている人はいないレベルだと思うのだが、Mimir YokohamaやInflatonでは使う場合がある。 その理由は、コマンドラインツールの利便性や説明の統一を狙って、Git for Windowsをインストールし、Git Bashを使う、という手順で説明することがあるからだ。

サーバー側はLinuxだし、zipでは返しづらい部分もある。こうした理由から手順が統一できるsharを使う…ということをしていたのだが、 現在は「Git for Windowsにtarが入っている」ことに気づいたのでこれも廃止した。

Baloo File Extractorの大暴走

顛末

BalooはKDE5のファイルインデックス機能。 かなり性質は違うものの、部分的に見ればKDE4のNepomukを置き換える機能だと私は見ている。

先日、突然デスクトップが非常に重くなった。操作に対して数秒反応しないということを繰り返す。 私はConkyで常に状態を見ているのだが、Baloo File ExtractorがCPUを2.5%(=ロード1.0)使用しており、 IOも非常に高い。しかし、止まるほどではない気がするのだけど…と思い、嫌な予感がしてjournalctlしてみたら

11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.
11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.
11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.
11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.
11月 19 08:29:06 hydrangea baloo_file_extractor[2677]: qt5ct: using qt5ct plugin
11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.
11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.
11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.
11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.
11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.
11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.
11月 19 08:29:06 hydrangea baloo_file_extractor[2677]: inotify_add_watch(/home/haruka/.config/fcitx/dbus) failed: (No space left on device)
11月 19 08:29:06 hydrangea baloo_file_extractor[2677]: inotify_add_watch(/home/haruka/.config/fcitx/dbus/4b235cc9521d448a9769a6f507904e37-0) failed: (No space left on device)
11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.
11月 19 08:29:06 hydrangea baloo_file_extractor[2677]: QFileSystemWatcher::removePaths: list is empty
11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.
11月 19 08:29:06 hydrangea baloo_file_extractor[2677]: QFileSystemWatcher::removePaths: list is empty
11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.
11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.
11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.
11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.
11月 19 08:29:06 hydrangea baloo_file[2401]: KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.

地獄だった。 要はBalooがinotifyを食い尽くしてしまっていて、カーネルレベルでのエラーを(他を巻き込みつつ)無限に繰り返している、と。

というわけでBalooを止める。完全に。

$ balooctl disable
$ rm -rf .local/share/baloo
$ systemctl reboot

インテグレーテッドシームレスUXの代償

最近はOSが統合的な環境を提供するというのはひとつの流れになっていると思う。 やり始めたのはAppleだと思うけど、ユーザーの囲い込み勝負になっていて、WindowsだってなにかしようとすればWindowsのソフトウェアが自動的に起動する。 そのインターフェイスにもCortanaを使わせようとするし、各コンポーネントが密結合し、「全体でひとつ」に見せようとしてくる。

これが良いことなのかどうかは、ちょっと私には判断しかねる。 少なくとも理想的な挙動を考えればシームレスで統合されているのは美しいことだ。 だが、現実はそうはいかない。気に入らないソフトウェアや気に入らない挙動、そしてもっと好きなソフトウェアというのはどうしたってあらわれるものだから。 KDEが統合的かつ多角的に機能を提供してくれることは、私は好ましいことだと思っているが、ではそれに満足しているかというと、実際のところKDEPIMですら「余計なもの」だと感じている。KDE TelepathyではなくPidginを使っているし、KMailでなくClaws Mailを使っているし、Akonadiに至ってはいらないとすら思っている。

そして何より気持ちわるい。 AppleやMicrosoftやGoogleが統合的に機能を提供しようとするのは、ユーザーのUXのためではないし、単なる囲い込みのためでもない。 ユーザーの情報を集約し入手するためだ。 FacebookがなんでもかんでもFacebookを経由して利用させ、かつFacebookを常に開いておくように要求するのもそのためだ。 そういう意図を持っているのがわかっているのに、情報を集める機能があるというのは、私はすごく気持ち悪いと思うのだ。

KDEコミュニティはそのようなことはないだろう。なんといっても、情報を集約したところでその使い途がない、というかそもそもそんなサーバーがない。 サーバーがあったところで、ではそのデータは誰の所有物かという問題になる。 だからKDEに情報収集機能があってもそれほど問題はない…はずだが、それでも私はNepomukも気持ちわるいと感じていたし、Balooもそんなに歓迎していない。

そもそも、私の感覚からいえば、ファイルインデックス系の機能というのは無駄なコストだ。

基本的にファイルは属性に応じてちゃんとヒエラルキーを持って管理しているし、「フォルダをまたいで特定の条件でファイルを抽出したい」というケースはかなり稀だ。 もちろん、全く役に立たないとは言わない。その稀なケースでは役に立つわけだから。

だが、支払うコストがそれに見合ったものではない。 基本的にファイルインデックス系の機能は常に非常に多くのアクセスを行うし、CPU時間も長い。 つまり、ファイルインデックス機能が機能するために必要とするIO及びCPUのコストは極めて高いのだ。

ご存知の通り、Windows Updateは「Windows Updateをするべき余地がない」ことが確定しない限り非常に重い動作になる。 どれほど高性能なコンピュータを使っていても、ほぼ使い物にならない(マウスカーソルも止まるし、メニューも開けない、文字入力もひどいラグが発生する)状態になる。 常にWindowsを立ち上げっぱなしの人であればそれほど気にならないだろうが、Windowsをそれほど起動しない人にとっては、Windowsは常に操作をうけつけない印象になる。 これはアップデートのための話になるが、もちろんアップデートのためにこのようなアクセスを行うことは優れたデザインではない。

このような、操作を明らかに妨げ、コンピュータが使い物にならなくなるような、利用可能な時間が制限されるような、ディスクデバイスの寿命を縮めるような、コンピュータが著しくストレスを生じる性能であるかのように思わせるようなコストを払ってまで必要な機能ではない、と私は思うのだ。

KDEに関してはNepomukでも同じようなことを経験したはずだ。CPUを食いつぶし、IOを食いつぶし、メモリを食いつぶしてコンピュータが使いものにならず、どれほど崇高な理想を掲げたところでコンピュータが使いものにならなくなればそれは意味がないということを思い知ったはずだ。 Nepomukは結局ほとんどのプログラムに利用されることなく終わったし、KDE5には継承されなかった。 にもかかわらずBalooを採用した。いや、Nepomukでの失敗を踏まえて改善したものを投入したのかもしれないが、そもそもNepomukがなぜ失敗したのかということを理解していない、結局わかっていない。 実際のところBalooについて調べると「お前を消す方法」で溢れてしまうのがその証左だろう。

「優れたUXはあれこれお節介を焼くことではなく、余計なことは何もしないことである」ということに、 一体いつ気がつくのだろうか。

Libreofficeが超絶重くなった

Libreofficeがドキュメントを開くとものすごく重くなった。だいたいフリーズというレベルである。

どうもglibとの組み合わせによるようで、今年の頭くらいには発生していた問題だったようだ。 私の環境ではいままで顕在化してこなかった。

CPUが2.50%で張り付いている。 私のマシンは40コアなので、要はコア占有状態ということだろう。

glibの問題ということでUIテーマを変更すれば解決するようだ。 具体的には$SAL_USE_VCLPLUGIN環境変数により使用するUIエンジンを強制する。 選択肢は優先度順に

  1. gtk3
  2. gtk
  3. gtk3_kde5
  4. qt5
  5. kde4
  6. gen

であるようだ。

で、glibの問題なので要はデフォルトでgtk3が使われてしまうけれど、gtk3を使わせなければ良い、と。

私の環境はそもそもKDE Plasmaベース (デスクトップはCinnamon) なので、KDEを使わせたいのだが、 現状Arch Linuxのlibreofficeはビルド時に--enable-qt5していないため、qt5が使えない。 もちろん、kde4は今や使えない。gtk3_kde5してしまうと結局同じ問題が発生する。 (gtk3との違いはファイルダイアログを表示すればわかるだろう)

そこで、SAL_USE_VCL_PLUGIN=gtk libreofficeとすれば正常に動作する。gtkでなくgenでも良い。

以前から割とgtk3テーマのlibreofficeは重かったので、もしかしたら常に強制しても良いのかもしれない。

ここらへんの情報はArchwikiでも古くて間違っているので困る。記述も雑だ。

Libreofficeは滅多に使わないのだが、請求書とか発注書とかはLibreofficeで書いている。 MS Officeと同程度にはやっぱりストレスがたまる。

この手のソフトウェアとしてLibreofficeよりも良い選択は恐らくない。AbiWordもCalligra Officeも機能的あるいは実用的に遠く及ばない。 にしても、やっぱりLibreofficeできれば使いたくないところだ。 紙面ベースのレイアウトをする場合はHTMLもそんなに快適ではないのでやむなく使ってはいるが。 (この手の書類は見た目だけの問題で意味的に整理する必要は全くないし)

とりあえずこうしてUIエンジンをかえられることを知っておくと今後もなにかの役に立つかもしれない。

Vivaldi 2.0

Vivaldi ウェブブラウザのバージョン2.0がリリースされた

動画にキレイな日本語字幕が入っていたりしてとてもいい感じだ。

Vivaldiは1.15から2.0に上がった形になる。 初期は不具合もそれなりに多く(それでも初期としては非常に良い出来で期待はできたが)開発者も少なかったので先行き不安なところもあったが、今やちゃんとしたブラウザになった。 今回の修正ではAltの無効化が大きい。VivaldiはAltがキャプチャされても(例えばスクリーンショットとか)メニューを開いてしまうので、この問題が(正しい解決方法ではないが)解決されるのは非常に嬉しい。

Vivaldの成り立ちは若干複雑で、Operaがコミュニティを捨ててただのChromiumに成り下がったことに納得が行かない人たちが出ていって作ったブラウザだ。 ところが、VivaldiはChromiumのフロントエンドでしかないし、コミュニティ機能のほうも当初から随分後退して「よくある程度よりも消極的」というレベルまで落ちてしまっている。

ではVivaldiに価値がないのかというとそんなこともなくて、中国企業に買収されたこともあってか不透明な振舞いを繰り返すOperaや、不信感を煽るような行為の多いGoogleと違ってクリーンでオープンという空気は保たれている。 Mozillaも多分に政治的になってしまっている現状において、Vivaldiは数少ない信頼できるオープンなブラウザだと見ていい。 このあたりはTakaakiさんのブログで詳細に説明されている。 また、マイナビの記事もなんとなくその空気が分かる。

また、VivaldiはChromiumをベースとしたブラウザとしては非常に珍しく、フロント部分はほぼ自前になっている。 Braveなどもそうなっているけれども、これによってVivaldiだからこそ使える部分も大きい。なにより、プライバシーを脅かすかもしれない要素は廃してコストをかけて自前で用意するあたり気合が入っている。

以前独自エンジン開発も辞さず、みたいな記事があったような気がしたけれども、それは見つからないので置いておこう。

この素晴らしいブラウザは使うほどに手に馴染む。 もしまだ使ったことがないという人がいたならば、ぜひ一度使ってみてほしい。

DPIと画面サイズとスケーリングとピクセルの話

概念の確認

今やあまり意識されることはないのだが、改めて言うと

  • ディスプレイは多数のドットからできている
  • 1インチ辺あたりいくつのドットがあるかというのがDPIである
  • であるから、DPIというのはXYがある。 ちなみに、ThinkPad X1 Carbon 2017は143x158DPIである。 (ppiといったりもする)
  • このためドットの実寸(正確に言えばドット間隔の実寸)はドット数が多いほうが、そしてディスプレイサイズが小さい方が小さくなる
  • ドット数が一定である場合、ディスプレイが大きくなるほど密度(解像度)は下がる
  • 逆にディスプレイサイズが一定である場合、ドット数が増えるほど密度(解像度)は上がる
  • ピクセル数はディスプレイ表示上の絶対数で、その実寸は変動的
  • cm, inch, mmといった単位は実寸上の絶対値で、実際に何ピクセルで描画するかは変動的
  • ラスターグラフィックス、あるいは固定的ピクセル数で描画されるUIは解像度が向上すると実寸としては小さくなる
  • ベクターグラフィックス、あるいは固定的実数値で描画されるUIは解像度が向上すると描画に使用するピクセル数が増加する

論理上の振る舞い

もともとWindowsは96dpiに固定的に設定されていた。 つまり、実際のディスプレイのDPIがどうであるかということに関係なく 1inch = 96pixels という計算をしていたわけだ。 これにより実数をピクセル数に変換していた。

このことから、また固定的ピクセル数でUIが書かれていたことも相まって、基本的に「解像度が上がるとありとあらゆる部品が小さくなる」という現象が起きていた。 Macも72dpiに固定されていて、同じような事情だった。

Windowsは可変値だが、現在Macは72dpiとして計算しつつ、1ポイントは2pxで表示するように変更された。

正しいのは明らかに正しいDPIを設定できるようにすることである。 というよりも、ディスプレイサイズとピクセル数がわかっていれば正しいDPIが算出できるはずだ。 ディスプレイサイズを論理サイズ(24インチとか)から算出するのは難しいが、そもそもどの製品も同じではないので、メジャーで測れば1分もかからない話だ。

実際、Linux(Unix)においてX Window Systemはそのような方式を取っている。

そしてその上で実寸値で指定するか、あるいは画面に対する相対値で指定すれば画面密度によらないスケーラブルな指定が可能だ。

一般的にウェブUIはピクセル数で指定されている。 文字のような流動的情報はインチで指定するほうが一般的だが、これに従うと高解像度ディスプレイではUIは小さいが文字の大きさは正しくUIに書ける文字が減る、ということになる。

現実のスケール

ところが、現実はそうなっていない。

仕方ない部分もある。 例えばパソコンの場合は、13インチでも15インチでも、21インチでも24インチでも「だいたい100から115dpi」あたりに落ち着く。

これを基準にすると、5インチやそこらで400dpiを越えるスマートフォンが困ってしまう。

これは、パソコンを基準に実寸値で表すと、例えば「幅10インチのウィンドウ」といった場合、5インチのスマートフォンでは2画面分の幅があるすさまじく見づらいUIができあがる。

対してピクセル数で表すのもかなり危険だ。 例えばMimir Yokohamaのウェブサイトは900px幅であり、FullHD液晶の場合半分よりちょっと小さいくらい1なのだが、例えば手元のAxon7の場合WQHDなので幅は1440pxある。だから画面の幅の1/3くらい、実寸では5.18cm = 2.03937inchだが、900pxというと3.2375cmになる。

3.2375cmのウィンドウ、しかもサイドバーつき。視点から近いといってもあまりにも小さい。

スマートフォンをパソコンと同じ基準にするのは、実寸値だろうがピクセル数だろうが、かなり難しいわけだ。

じゃあどうしたか、というと、1px=1ピクセルじゃなくしてしまった。

なにを言っているか意味がわからないだろう。

実はMacも同じで、「1ポイントを2ピクセルで描画する」といったが、実はMacでは「1ピクセル指定しても実際は2ピクセルで表示する」ようになっている。 「密度が高いから従来の倍で表示しちゃおう」というわけだ。実際、Retinaディスプレイは180dpiとかあるので、従来のラスターUIをベクターUIのように扱って144dpi相当で描画されることはそれほど困らない。

技術に明るくないウェブエンジニア諸兄はCSSにこんなことを書いているかと思うが

何も疑問に思わないのだろうか。 1080pxをこえているならスマートフォンでは満たさない可能性が高いが、799pxなら今のスマートフォンは大概満たしているだろう。 だが、実際スマートフォンでこれが適用される可能性はかなり高い。

基本的に現代では96dpiを基準にスケールしている。 つまり、実際には180dpiくらいあるディスプレイなら、1ピクセルを倍で描画する。言い換えると、あたかもピクセル数が半分しかないディスプレイであるかのように扱う。

これは正しいのだろうか?

実際は、結構困る。 というのは、UIでピクセルで指定できない場合というのは普通にあって、この場合画面全体のピクセル数は「本当のピクセル数」を回答する。 これに基づいて割合を決めてピクセル数指定すると実際ははみ出してしまったりするのだ。

また、例えばThinkPad X1 Carbon 2017だと約1.4896倍にスケールする2のだが、900pxは1341px、1080pxは1608pxで表示されることになる。 ディスプレイ全体では1920pxなので、もちろん画面には収まるのだが、タイルした場合は1341pxは画面の半分に収まらないのでサイドバーは表示されなくなる。 「タイルしたときにはギリギリサイドバーが表示されたほうが良い」という私の考えは無碍にされてしまうわけだ。

これは12インチクラスのモバイルラップトップだとさらに厳しい。ThinkPad X280は12.5inchなので計算上は10.9inch->176dpiとなり、 12.1inchのLet’s Note SZに至っては10.5inch->182dpiとなる。 この計算においては、ThinkPad X280の場合幅1047px扱いとなり、1080pxですら満たせない。Let’s Note SZに至ってはギリギリ1000pxを満たす程度だ。

色々難しいのには理解を示したいところだが、現実問題としてこれは望ましくなく、「自分に使いやすい嘘DPIを設定する」以外になくなってしまう。

また、この事情により、以前はスケーリングを想定してフォントサイズはピクセル数からポイント数へと動いてきたのだが、 現在はスクリーンフォントはピクセル数で指定したほうが柔軟にスケールしてくれたりする。

個人的な所感

嘘DPIを設定するようでは、せっかくDPIが設定できることが無意味になってしまうし好ましくないと思う。 そもそも、96dpiを基準に固定したまま何倍にしよう、というのはVGA時代の失敗の繰り返し以外の何者でもないとも思う。

おおよそ正しいアプローチなのだから、DPIとスケール倍率という概念を分離できなかったものか。

実はGNOMEはDPIの設定がなくて、DPIを一切尊重しない。 そのかわり

$ gsettings set org.gnome.desktop.interface scaling-factor 2

のようにしてスケールできるのだが、このスケール、整数しか指定できない。 せめて1.5を指定させてくれとものすごーく言われているのだが(13.3inch, 14inchクラスでFullHDだと140から150程度なので1.5がちょうどよい)、対応の気配はない。3

一方、KDE PlasmaはGTK+アプリケーションであっても適切にスケールしてくれるが、あくまでDPIによって自動的にスケールするだけで倍率の指定はできない。

現状で理想的なのは

  • DPIは正確なDPI値を算出する。この状態ではinchやcm指定は厳密に適切な値で表示される。 グラフィッカーには便利な状態
  • これによって自動的にRASTER_SCALEACTUALSIZE_SCALEが表示として倍率として設定される。これによって現在と同じ状態になる
  • RASTER_SCALEはディスプレイの論理ピクセル数に影響する。 つまりRASTER_SCALE=2の状態でFullHDディスプレイなら、水平ピクセル数は960だと回答する
  • RASTER_SCALEを手動設定可能とする。ユーザーは自動設定された値(デフォルト値でもある)を基準に好みに合わせて動かす。これに伴うディスプレイの論理ピクセル数も表示すると親切。
  • グラフィッカーのためにACTUALSIZE_SCALERASTER_SCALEと切り離して設定可能とする。ACTUALSIZE_SCALE=1にすれば1インチは1インチで正しく表示してくれる。

だと思うけれど、きっと誰も聞き入れてくれないだろう…

なお、4kディスプレイにしたWindowser向けにアドバイスとして「このままだと文字が小さいので解像度を1920×1080に設定すると解決するよ」と言っているのをかなり見るのだが、もはやこれは頭痛が痛い…


  1. 「半分よりも小さい」のは、フルHDディスプレイでタイリングしたときにサイドバーを表示させるためだ。

  2. これは、X Window Systemで正確なディスプレイサイズを入力し、精密にスケールできるKDE Plasmaを使った場合の話である。

  3. この記述について指摘があり、GNOME 3.26から実験的機能として25%単位のスケーリングを可能としているとのことだった。 YouTubeにも動画が上がっていたりするが、実際に試したところかなり粘ったものの値自体は入れることができるものの依然として「100%」「200%」の選択肢のみであった。Nvidiaでなければいけないなどの制限があるのかもしれない。

Windows 10について

悪名高いWindows 10をある程度使ってみている。
とりあえず、それで思ったことをまとめてみよう。

OSとしての進化

一般の人の中にはOSの進化を頑なに認めない人がいるが、元々Windowsが(それ以前にMS-DOSが)ひどい代物である以上、修正すべき点は多い。低い生産性は人類の進歩を妨げさえする。

だから、Windowsはどんどん改善してもらわないと困る。

純粋にOSとしての点をみれば、それが正しいかどうかはともかく、かなり良くなっている。
「ここはこれがおかしくて、こうあるべきだからこうしてほしい」という点は山ほどあるのがWindowsだが、その望まれているあるべき解決方法ではなく、なぜか全然違う方法で提供して、「なんでそうなった!」と突っ込まれるのは恒例なのだが、今回もそのような変更点は非常に多い。

いつまで経ってもfork(2)をサポートしないこととか、まともなシェルを用意せずに、明らかにシェル、端末として使いようのないPowershellを導入したまま放置していることとか。

私はWindowsアプリは書かないし(書くとしてもクロスプラットフォームレイヤ上で)、コアな開発者でもないため感じることは少ないが、「クロスプラットフォームレイヤのWindows環境下での制約」という形で困る。

そういう内部的なことは置いておくとしても、Windows7以降で改善された点として

  • ISOファイルのマウントが可能になった(Win8)
  • 通知機能がまともになり、通知履歴が見られるようになった
  • すべてのUI部品が(一部は仮想的に)スケーラブルになった
  • Win + cursorによるquick tile機能が8方向タイルに対応した(現在位置に基づくタイル。Cinnamonと同じ)
  • IMEスイッチャが搭載された(Windows+Space)

使い勝手

Windows 10の使い勝手そのものは改善しているといっていい。

UIについて考えてみると、基本的な部分はWindows XPの時点で既に完成されていた。Windows 95の時点で基本的な部分はできあがっていたが、見た目がチープであることと、たどりにくい階層構造が難点だった。
Windows XPではテーマ制を導入(Visual Styleの変更はパッチ当てが必要)、簡易なものではあるが、デスクトップ検索も追加している。
また、ウィンドウのタイル表示も可能になった。

Windows Vistaでさらなる改良が行われたが、本質的な進化というよりも、変化であるという面も大きかった。
Windows 7でアイコン式のタスクバーになったが、これも元よりそうであるべきだったというものではない。選択できるなら機能追加だが、選択できないので単なる変更だった。

明らかに改良された点は検索機能が強化され、ポータブルアプリの起動にランチャが不要になったという点だが、スタートメニューの構造やタイルの仕様変更などは、変更でしかないように感じられた。

ちなみに、Windows 7からはWin + Cursorによるquick tileが可能になった。ただし、Windows 7では左右のみだ。

Unix系デスクトップはこの時期、何を取り込んだのか先行したのかわからない機能を数多く導入している。quick tile機能はKDE4, Cinnamon 1.0の導入なので恐らくWindows 7のほうが早い。入れ替え式階層メニューはWindows Vistaが早いが、KDE Plasma 4のものはまた違うし、Cinnamonもさらに違う。また、MacのExpose相当の機能はCompizが入れたため、Windowsよりも早い。

だが、Unixデスクトップで考えても、機能的にはGNOME 2あるいはKDE3で既に完成していた。これらはWindows 95のUIを参考に発展したものであり、その後は細かな機能追加に過ぎないと感じている。

変化を求めている部分もあるし、変化に応じている部分もある。だが、どちらかというと、Microsoftは変な先読みをしている、というよりも自分がスタンダードを作るのだという妙な気負いがある。

それが、Windows 8でのスタートメニューを廃止してModarn UIを導入したことにあらわれているのだろう。奇抜だが、真新しさの演出と、Windowsがやればそれが普通になる、という考え方に基づくのだと思う。

結果として失敗した。実際に問題点が多く、使いにくかったということもあるが。

Windows 10のスタートメニューはその修正だと考えていい。スタートメニューとModarn UIを一体化させたものは、確かにある意味では使いやすいが、目新しいものであるというよりは、Androidスマートフォンで見慣れたものになった。

Windows 8からのフラットUIもより推し進めてはいるが、やはりデザイン的にリッチさに劣るように(個人的には)感じられるし、特に見やすくなったわけでもないため、単なる変更だろう。

機能が一体的に提供されるようになったために、それに従うかどうかだが、「Microsoft製のOS(Windows)で、Microsoftが推奨する音声エージェント(Cortana)を使用し、Microsoftのサーチエンジン(Bing)を介して、Microsoftのブラウザ(Edge)で開く」のであれば使いやすいのだ。

だが、これはAndroid同様のリスキーな面を持っている。

ケータイ化・個人情報商売という悪夢

Windows 10において問題なのは使い勝手ではなく、この2点だ。

Windowsは統合的な環境になった。基本部分ではなく、ユーザーのすべてを「Windowsが」提供しようというのだ。

そこには、メールやスケジュール、あるいはSkypeやTwitter, LINEさえも含まれる。
WindowsストアアプリはWindowsから独立ではなくなった。言い換えると、Windowsストアアプリに提供されている情報はMicrosoftにも与えられる、という形になった。

Windowsに、言い換えればMicrosoftに依存して生活するのであればこれは便利な機能だ。もちろん、Windows Phoneも一緒に。検索エンジンはBingだ。

だが、これはありとあらゆる情報的ライフライン及び情報そのものをMicrosoftに掌握させることを意味する。事実、Windwos 8.1ではデフォルトでオフだった、利用状況や入力内容の送信など、プライバシー上重大な懸念のある機能が、全てオンの状態になるようになった。

しかも、コアな利用状況のレポートは、送信をオフにするとWindowsが動作しないという理由をつけて、必ず送信させる。

ありとあらゆる情報を収集し、個人を監視するような行動は、これまでGoogle及びAppleがとってきたものだ。それと比べるとMicrosoftは穏やかなやり方をしてきた。

だが、今回Microsoftは積極的にプライバシーを手に入れようとしている。Web, Twitter, Facebook, Skype, LINEはもちろん、電話からメールまで全てだ。

それに合わせて規約も、メールの内容を読むというものになった。
また、利用には基本的な部分でMicrosoftアカウントと紐付ける必要がある。完全に個人を特定し、追跡できるようにするものだ。

これらのプライバシーに対する重大な懸念を、私は今回最も問題視する。

また、ケータイの場合はそうした統合的な機能を求める傾向があるのだが、Windows 8で学んだはずの「スマートデバイスとの融合は不自由を生む」ということを無視して、より融合を進める方向になった。

電話だのSNSだのといった機能は、Skypeと連動させることでパソコンでも利用可能なのかもしれないが、明らかにコンピュータの使い方としてそれは重要な部分ではない。それを中心に据えられるのは非常に迷惑だ。

また、パーソなりゼーションを進め、SNSの情報を常に表示し、ニュースを表示し…といった機能は、ビジネスシーンで使われるWindowsということを一切無視しているとしか思えない。

その意味で、ものすごく使いにくくなった。

KDE Plasmaも、KDEアプリケーションを使ってこそという部分はある。KDE PIMとAkonadiだ。
だが、使わないという選択肢はある。その場合、Plasmaを使う魅力は大いに損なわれてしまうのだが、普通のデスクトップ環境としては使うことができる。

今回のWindowsは、その選択権がない。ちなみに、しょっちゅうデフォルトのアプリをMicrosoft製のものに変えられるようになった。ブラウザがEdgeにされるのは日常茶飯事だし、IMEはデフォルトを完全に無視してMS-IMEを選択する。

アカウント

Windows 10ではアカウントが、コンピュータ上のローカルアカウントとMicrosoftアカウントの二種類がある。

ローカルアカウントをMicrosoftアカウントに接続することで、ローカルアカウントとして使いながらMicrosoftアカウントを要求するストアアプリを利用するといったことも可能だ。

だが、Microsoftアカウントでは、ログイン(今回からローカルアカウントでも「サインイン」という表現になった)時にオンライン認証をする。
そのため、インターネットに接続されていなければコンピュータを利用すること自体できなくなった。

デスクトップアプリとストアアプリ

ほぼ全てのプログラムに「デスクトップアプリ」と「ストアアプリ」という区別ができた。

Windows 8にもあった区別ではあるが、ストアアプリを使う機会が設定しかないということもザラにあったため、目立たなかった。

今回、多くの機能がストアアプリに移行したため、この区別が重要な意味をもつようになった。また、ストアアプリがウィンドウ表示できるようになったというのも大きい。

これにおいて重要なのは以下の点だ。

  • ストアアプリは事実上Microsoftが全権を持っている
  • ストアアプリではIMEがストアアプリに対応しているMS-IMEしか使えない
  • ストアアプリはオンラインアカウントとひも付けて実行される

表面から消失した機能

コントロールパネルが隠されてしまっている。
これは検索から起動できる。

これは、ストアアプリの設定を使わせるということなのだろうが、全項目があるわけではなく、重複があったり、片方にしかないものがあったり、表現に整合性がとれていなかったりと非常にわかりにくくなった。

設定に関してはgodmodeも追加された。これは、コントロールパネル的なデスクトップアプリのフラット版だ。

まだ、ログオフがメニューから消失した。
Windows 10はオンラインアカウントでのログインを原則としているため、サインアウトに変更されている。

logoffコマンドが存在するため、logoffで検索すれば抜けられる。あるいは、CAD(Ctrl+Alt+Delete)からでも良い。

総括

OSとしては妥当に進化した。あまりにもスマートフォンを意識しすぎて、デスクトップコンピュータとしての使い勝手が著しく損なわれた程度で、別に悪くなったとは言わない。

全てはログインを「ログオン」という表現から「サインイン」という表現にかえたことにあらわれていると思う。

今やWindowsを使うということは、Microsoftのサービスを利用するということとイコールなのだ。

これまではWindows上で他のサービスを利用することも当たり前だった。だが、これからはそうではない。WindowsはMicrosoftのサービスを使うことを強要する。Appleがそうしたように、ロックインして競合する他のサービスの利用を不能とする可能性もないわけではない。

既に、Windowsを起動すれば当然にGoogle Chromeが起動し、Google日本語入力で文章を打ち…ということはできなくなった。Edgeに変更された設定を戻し、Google日本語入力に切り替えなくてはならない。

また、Windows上で行う作業は当然に秘密が保たれていると信じていただろう。これからはそうではない。Windows上で行う作業は、須くMicrosoftが知りうるものなのだ。

こうしたことをどのように考えるかによって、Windows 10が良いものか悪いものかの判断ができるだろう。

Mikutter

Mikutterが2.0.6になったのでアップデートするついでにプラグインを導入してみた。

Mikutterのプラグインは原則~/.mikutter/plugin/pluginnameディレクトリに導入する。このpluginnamepluginname.rbを読む仕様のため、これに合わせなくてはいけない。

ほとんどのプラグインはGitHubで管理されており、git clone URI.git ~/.mikutter/plugin/pluginnameで大体はいける。そうでなくてもそのような形式のディレクトリに一式ぶちまければいける。

しかし、当然ながらそれによって依存関係の欠如が生じる。例によってMikutterリポジトリでbundle installすればいいのだが、いくつかのGemファイルがエラー終了してしまう。少しハマったがよくよく調べてみるとruby-develが入っていないということだった。

プラグインを利用していないのでまだその効果のほどは分からないが、Userconfig Accessorはロードするとクラッシュしてしまう。

操作系プラグインはごく単純なものが多いが、これを見ると自分で書くのも難しくはなさそうだ。ただし、「何にアクセスするか」という問題は出るだろう。

しかし、個人的にはRubyスクリプトであることが非常に助かる。ローカルな対応のためにソースを読んだり、挙動を確認するためにソースを読んだりできるからだ。

Linuxトラブルとの格闘

15日、Manjaroが起動しなくなった。systemdが途中で沈黙してしまう、という症状だ。

systemdが、となるとそう簡単ではないため、とりあえずMageiaに戻ったのだが(もちろん、home directoryにおける様々な問題が生じた)、17日、そのMageiaに致命的な現象が生じた。

極めて重くなったため、topしてみると、nepomukfileindexなるプロセスが非常に激しく動いている。これはなんだ?

とりあえず再起動してみると、fork bombが発生した(konsoleが27 windows開いた)。nepomukのせいか!?

ファイルアクセスが激しいということはデータを盗み出す類のプロセスである可能性が高いと判断し、直ちにイーサネットケーブルを抜いて、別システムのインストールを行うことにした(今インシデントを扱える状況ではない)。

Manjaroを潰してSSDにMageiaをインストールすることにしたのだが、Btrfsが扱えない、LUKSをかけるとパスワードが入力できないなどのトラブルで何度も再インストールを余儀なくされた。

なお、Manjaroの再インストールもこの過程で何度もトライしているのだが、pacman-key –populateに失敗するためどうしようもなかった。どうもForumにあるこのケースと同様のようだが、相当苦戦しているで容易ならざることか。このフォーラムでも解決していないようだ。

特に注意してほしいのが、インストーラでキーボードレイアウトにJPを選ぶとJPキーボードでインストールされる。ところが、ログインするとその状態ではinput methodが何も設定されておらず、iBusはUSキーボードのレイアウトで入力させる。そのため、記号を含むパスワードを設定しているとキーボードレイアウトの違いからlogin incorrectとなる。

なお、KDMではJPキーボードでインストールしているのであれば自動的にJPキーボードの配列で入力することになり、ログインは通常通りできる。このことでだいぶハマった。

しかしセットアップしていくと、再びnepomukが顔を出す。何者なのだこいつ。

そこでrpm -qfしてみると、nepomuk, nepomuk-core, kde-coreに含まれていることが分かった。調べてみると、まっとうなプロジェクトで、それを積極的に利用している例がKDEだという。

ではあのfork bombとは別ものだったのか。あの妙な挙動は何だったのか。

分からないのだが、とりあえずは安心していいのだろうか。もちろん、virus checkもすませてあり、問題はほぼなかった。少なくとも懸念していた問題はみつからなかった。漏洩調査が必要だが、これはかなり難しい。また、汚染調査の追跡は、これから行うことになる。これもしばらくかかるだろう。パスワード変更作業などが迫っている。

ついでなので、インストール時点で/varを別に切りLVM上に置く、SSDにインストールするなどの懸案を片付けた。また、これまではhome directory(/homeとは別に、/home/user)にマウントしていたのだが、dor filesに非互換が生じることがあるため、この解決のために/home/user/shareにマウントするようにした。共有すべきdot filesをsymlinkにすることでdor filesなどの共有問題を解決し、基本的にはデータ共有とすることで問題及び影響を生じにくくした。

share下にあるディレクトリの設定上の問題(~/binなどでもある)が生じるため、従来通り~/localのpathを維持するため、~/localは../share/localのsymlinkとした。

今回の作業でよくわからない変化があった。

まず、gtk-query-updateをしてFirefoxで正常に日本語入力が可能になった。これまでなぜできないのか分からず様々な方策を試しているような状態だったのにだ。一方、meditはIMEは機能するが入力できない状態、つまり変換も確定もできない状態となっている。