Gitでローカル・リモート一緒にブランチ名を変更したい

Git 2.11.1で確認。

シチュエーション

hageってブランチ作って、

$ git checkout -b hage

諸々修正して、

$ git commit -a
$ git push -u origin hage

commit・pushした後に、

「あれ、ブランチ名間違えた。hogeだった」

ということで、ローカルとリモート併せて名前変えたい。

失敗例

upstreamブランチをリモートから削除して

$ git push --delete origin hage

ローカルブランチの名前を変えて

$ git checkout hage
$ git branch -m hoge

そのまま再push!

$ git push -u origin hoge
...
 * [new branch]      hoge -> hage
                             ~~~~

あれー?

$ git push --delete origin hage
$ git branch -vv
...
hoge ff0da9d [origin/hage: gone] ....
...

リモートのブランチが消えても、一度設定されたupstreamの向き先は残ってるっぽい。

解決

upstreamブランチを削除してローカルブランチ名変えた後、明示的にupstream設定を削除する必要があるようです。

$ git branch --unset-upstream

その後、再push

$ git push -u origin hoge
...
 * [new branch]      hoge -> hoge
                             ~~~~

めでたしめでたし。

atom 1.9.x + graphviz-preview の表示バグの回避

Atom を使っていて、ちょっと図を書いて整理したいなんてときに便利な graphviz-preview ですが、2016/8/27現在、graphviz-previewの更新が、対応バージョン1.7.0で止まっており、現時点の最新である1.9.8で使用するとこんなことになってしまいます。

f:id:shout_poor:20160827195503p:plain

そのうち修正されてほしいなあと思いつつ、とりあえずの回避方法。

$HOME/.atom/packages/graphviz-preview/styles/graphviz-preview.lessテキストエディタで開き、以下のwebview要素のstyleを追記します。

// The ui-variables file is provided by base themes provided by Atom.
//
// See https://github.com/atom/atom-dark-ui/blob/master/styles/ui-variables.less
// for a full listing of what's available.
@import "ui-variables";

.graphviz-preview {
  background-color: #fff;
  overflow: scroll;
  box-sizing: border-box;
  padding: 0;

  iframe {
    width: 100%;
    height: 100%;
    border: 0;
  }

  // ここから追記
  webview {
    width: 100%;
    height: 100%;
    border: 0;
  }
  // ここまで追記

}

するとこんな感じに。 スクロールバーが二重で表示されるのカッコ悪いんですが、とりあえず使うのには困らないので、これでしのぎながら公式のアップデートを待ちます…

f:id:shout_poor:20160827200504p:plain

2016/08/28 追記

.graphviz-preview クラスと webview 要素に overflow: none; を設定してやると余分なスクロールバーが消えました。実際のスクロールバーは、webview内に貼られるobjectが表示してくれるようです。

// The ui-variables file is provided by base themes provided by Atom.
//
// See https://github.com/atom/atom-dark-ui/blob/master/styles/ui-variables.less
// for a full listing of what's available.
@import "ui-variables";

.graphviz-preview {
  background-color: #fff;
  overflow: none;      // scroll -> none
  box-sizing: border-box;
  padding: 0;

  iframe {
    width: 100%;
    height: 100%;
    border: 0;
  }

  // ここから追記
  webview {
    width: 100%;
    height: 100%;
    border: 0;
    overflow: none;
  }
  // ここまで追記

}

追記:Let's Encrypt のDV証明書更新

この記事の続きというか補足。

自サイトをHTTPS化してみた - NoisySpot/メモ帳

Let’s Encryptで発行されるDV証明書の有効期限は90日間と短めに設定してありますが、Certbotのクライアントツールを使用していれば更新は簡単。コマンド一発です。

$ sudo letsencrypt renew 

確認のためのDry runも可能です。(--dry-run オプション)

ただ、このコマンドは証明書が失効する前に実行しても、何もしてくれません。つまり証明書が期限切れ状態となる期間が発生してしまいます。

Certbotの解説によれば、cronやsystemdでの1日2回の実行が推奨されていますので、失効する期間を最小限にするためにも、こちらに従って定時バッチ化しておくのがいいと思います。

(2017/05/10 修正)

誤ってEV証明書と記載していた箇所をDV証明書に修正。今のところ、Let’s EncryptでEV、OV証明書は取得できません。

自サイトをHTTPS化してみた

はてなブログでホストしていただいている等ブログの他に、勉強のために自ドメインでサイトを構築しております。 (内容はあまりありません。日記的ブログだった残骸があるだけです…)

Noisy Spot | 個人的まとめ

で、なんか最近調子が良くなかったので、OSごと再構築しまして、ついでに Let's Encrypt のDV証明書を利用してHTTPS化してみました。

letsencrypt.org

以下は手順メモです。

作成時の環境

  • Ubuntu 16
  • Nginx 1.10.0
  • OpenSSL 1.0.2g

クライアントツールの取得と申請~証明書発行

Let's Encrypt への申請、認証、証明書発行の一連の流れを自動化してくれるクライアントツールは、対象の環境や実装手段によりいくつか開発されているようです。Let's Encrypt推奨の方法としては、以下のCertbotというサイトでWebサーバとOSを選択して、表示された手順に従うというものでした。今回はそれに従いました。

certbot.eff.org

まずは apt でクライアントツールを導入します。

$ sudo apt-get update
$ sudo apt-get install letsencrypt

で、nginxを起動した状態で、letsencrypt コマンドを打ちます。

$ letsencrypt certonly --webroot -w [[Webサーバのドキュメントルートのパス]] -d [[申請するドメイン名]]

すると、CUIが立ち上がり、メールアドレスを要求してくるので入力します。申請失敗時などはここで入力したアドレスに連絡が飛んで来るようなので、使えるアドレスを入力する必要があります。

メールアドレスを入力し、利用規約などをAgreeすると、申請・認証が開始します。以下の様な流れで動作するようです。

  1. メールアドレスとドメイン名をLet's Encryptに送付して発行申請する。戻り値としてシグネチャが返される。
  2. Webサーバのドキュメントルートに .well-known/acmechallenge/ ディレクトリを作成し、返されたシグネチャを格納
  3. Let's Encryptのサーバから、http://[[申請したドメイン]]/.well-known/acmechallenge/ のリクエストを飛ばし、取得したシグネチャと発行したものが同一となればドメイン所有者認証の完了
  4. 証明書と鍵がLet's Encryptから発行され、クライアントツールはそれらを所定の場所に格納する。

以上がコマンド実行から2分くらい放置で完了。簡単!

上記コマンド完了後 /etc/letsencrypt に下記のファイルが格納されます。

.
|-- accounts
|   `-- acme-v01.api.letsencrypt.org
|       `-- directory
|           `-- [[ハッシュコード]]
|               |-- meta.json
|               |-- private_key.json
|               `-- regr.json
|-- archive
|   `-- [[ドメイン名]]
|       |-- cert1.pem
|       |-- chain1.pem
|       |-- fullchain1.pem
|       `-- privkey1.pem
|-- csr
|   `-- 0000_csr-certbot.pem
|-- keys
|   `-- 0000_key-certbot.pem
|-- live
|   `-- [[ドメイン名]]
|       |-- cert.pem -> ../../archive/[[ドメイン名]]/cert1.pem
|       |-- chain.pem -> ../../archive/[[ドメイン名]]/chain1.pem
|       |-- fullchain.pem -> ../../archive/[[ドメイン名]]/fullchain1.pem
|       `-- privkey.pem -> ../../archive/[[ドメイン名]]/privkey1.pem
`-- renewal
    `-- [[ドメイン名]].conf

Nginxから参照するのは以下の2ファイルです。(再発行時にそなえ、ファイル実体を直接参照するより、live以下のシンボリックリンクを参照するほうがよいでしょう)

  • サーバ証明書 /etc/letsencrypt/live/[[ドメイン名]]/fullchain.pem
  • 秘密鍵 /etc/letsencrypt/live/[[ドメイン名]]/privkey.pem

この時点で、ドキュメントルートの .well-known ディレクトリは削除しておきます。

ちなみに、WebサーバがApache2等の場合、letsencryptのサブコマンド(上記の certonly)を run とすると、上記の証明書発行に加え、Webサーバの設定ファイルの更新までやってくれるようなのですが、現時点ではNginxはそこまでサポートされていないようです。

なので、以下はNginxへの個別設定となります。通常のhttpsサイト構築とあまり変わらないと思いますので、その辺ご存じの方は、サーバ証明書秘密鍵の格納場所がわかれば十分でしょう。

DHパラメータファイルの作成

TLSの暗号化アルゴリズムの一つ、Diffie-Hellman暗号で使用するDHパラメータファイルを作成します。

$ sudo openssl dhparam 2048 -out /etc/nginx/dhparam.pem

`2048' は生成するビット長です。数値が大きいほど生成に時間がかかりますが、小さいと脆弱になるので、とりあえずLogjam Attack対策での推奨値である2048を設定しました。うちの環境(2CPUのVPS)で1分くらいで生成完了。 ファイルパーミッションは600にしておきます。

Nginxの設定

/etc/nginx/sites-available/ 以下に任意の名前でファイルを作成し、サイト設定を記入します。

server {
       listen 80;
       server_name www.noisyspot.jp;
       return 301 https://$server_name$request_uri;   # httpにリクエストが来たらpermanent redirectでhttpsに飛ばす
}

server {
        listen 443 ssl default_server;
        listen [::]:443 ssl default_server;

        ssl_certificate /etc/letsencrypt/live/[[ドメイン名]]/fullchain.pem;    # サーバ証明書
        ssl_certificate_key /etc/letsencrypt/live/[[ドメイン名]]/privkey.pem;  # 秘密鍵
        ssl_session_tickets on;

        ssl_dhparam /etc/nginx/dhparam.pem;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers 'ECDHE+RSAGCM:ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:!aNULL!eNull:!EXPORT:!DES:!3DES:!MD5:!DSS';
        ssl_prefer_server_ciphers on;


(以下略)

ssl_protocolsssl_ciphers は、暗号化仕様の使用制限に関する設定です。今回は以下のサイトを参考にしました。

qiita.com

このへん、脆弱性が見つかっては新しい物が策定されが繰り返し、またWebサーバやブラウザ側のサポート状況にもよりますので、世間に注意しつつ定期的に更新していく必要があるでしょう。

あとは、/etc/nginx/sites-enabled/ から、上記で作成したサイト設定ファイルへのシンボリックリンクを張り、defaultのシンボリックリンクを削除すれば、設定は以上です。念のため nginx -t で設定ファイルのバリデーションをした後、 systemctl restart nginx サービスを再起動すればOK。

Emacsへのphp-mode, php-completion の導入方法

下記の参考資料通りにやってみたところいくつかエラーが出たので、試行錯誤して正常にインストールできた手順をメモ。

前提

必要なパッケージのインストール

M-x package-install-package でパッケージの一覧を表示し、以下のパッケージをインストールする。

  • cl-lib
  • anything
  • auto-complete
  • php-mode
  • php-completion

cl-libはphp-modeインストール時に一緒に入るが、それ以外は個別に入れること

init.elの設定

init.elに以下のスクリプトを追記。(入力補完は C-oバインドしていますが、他のキーにしたい場合は (kbd "C-o") の部分を変えてください)

(require 'php-mode)
(add-hook 'php-mode-hook
      (lambda ()
        (require 'php-completion)
        (php-completion-mode t)
        (define-key php-mode-map (kbd "C-o") 'phpcmp-complete)
        (make-local-variable 'ac-sources)
        (setq ac-sources '(
                   ac-source-words-in-same-mode-buffers
                   ac-source-php-completion
                   ac-source-filename
                   ))))

2016/07/02追記

別の環境でインストールしようと思ったら、パッケージリストにphp-completionが出てこなかったので原因調査中です… →php-completionパッケージはmelpaではなくmarmaladeにありましたので前提に追記。

参考資料

PHPを扱うようになったのでemacsにphp modeを入れてみた。 - Qiita

Java8 定義済み関数型インタフェース(主要なものメモ)

忘れるのでメモ。プリミティブ型用のインタフェースは省略。

Interface Scala風関数型表記
Consumer<T> (T) => void
BiConsumer<T, U> (T, U) => void
Supplier<T> () => T
Function<T, R> (T) => R
BiFunction<T, U, R> (T, U) => R
Predicate<T> (T) => boolean
BiPredicate<T, U> (T, U) => boolean
UnaryOperator<T> (T) => T
BinaryOperator<T> (T, T) => T
Comparator<T> (T, T) => int

Comparator<T> は、Java7の頃から存在する、ソート等での順位付けを行う比較関数を実装するための Interface のため、 java.util パッケージに属する。その他は java.util.function パッケージに属する。

無限Streamに終了条件を設定する

例えばDOMのようなツリー構造があったとして、あるノードを渡された時に、ルートからそのノードまでの名前を連結してパス文字列を作成する、というプログラムを考えます。

Java7でも動くように、副作用ループを使った書き方だとこんな感じ。

public static String getTreePath(Node node, String delimiter) {
    LinkedList<String> nodePath = new LinkedList<>();
    for (Node n = node; n != null; n = n.getParent()) { //根でgetParentするとnullが帰るものとする
        nodePath.add(0, n.getName());
    }

    return delimiter + String.join(delimiter, nodePath);
}

んで、これをJava8のStream APIで書くとどう書けるのか考えてみて、最初に書いたのがこれ。

public static String getTreePath2(Node node, String delimiter) {
    List<String> names = Stream.iterate(node, Node::getParent)
            .filter(n -> n != null)
            .map(o -> o.getName())
            .collect(Collectors.toList());
    Collections.reverse(names);

    return delimiter + String.join(delimiter, names);
}

半分くらい書いたところで、あれ?って思ったんですが、案の定これは NullPointerException で落ちました。Stream.iterate に渡した関数(Node::getParemt)を連鎖的に評価させてその結果をリスト化するわけですが、getParent が null を返しても、そこで評価を止めることができないわけですね。

API Document とかいろいろ漁って調べてみましたが、単純に Stream.iterate や Stream.generate で生成した無限 Stream は、limitで決まった数で区切るか、zip で有限 Stream と結合するか、短絡評価をする終端操作で区切るしかなさそうです。「指定した条件を満たした最初の要素より前のものだけ抽出」のような中間操作ないし終端操作はない模様。

こういう場合は、 Stream の作成のしかたからカスタマイズする必要があって、API Documentによると、「最も単純だが低パフォーマンスな」作成方法は、Iterator の実装クラスを作って、そのインスタンスから Splitterator を作ることとありました。なるほど、Iterator なら生成関数と終了条件関数をセットで実装できますね。

で書いてみたのがこちら。

public static String getTreePath3(Node node, String delimiter) {

    Iterator<Node> iter = new Iterator<Node>() {
        private Node n = node;

        @Override
        public boolean hasNext() {
            return (n != null);
        }

        @Override
        public Node next() {
            Node pre = n;
            n = pre != null ? pre.getParent() : null;
            return pre;
        }
    };


    List<String> names = StreamSupport.stream(
            Spliterators.spliteratorUnknownSize(iter, Spliterator.ORDERED), false)
            .map(o -> o.getName())
            .collect(Collectors.toList());
    Collections.reverse(names);

    return delimiter + String.join(delimiter, names);
}

長ぇ。

最初のループを使った書き方とくらべても断然めんどくさいですね。

Stream を作るところを、別のメソッドに切り出して、軽く汎用的にしてみます。引数としては、 Stream.iterate の引数に終了条件 term を加えた形ですね。

private static <T> Stream<T> iterateLimited(T seed, UnaryOperator<T> generator, Predicate<T> term) {
    Iterator<T> iter = new Iterator<T>() {
        T current = seed;

        @Override
        public boolean hasNext() {
            return !term.test(current);
        }

        @Override
        public T next() {
            T pre = current;
            current = generator.apply(pre);
            return pre;
        }
    };
    return StreamSupport.stream(
            Spliterators.spliteratorUnknownSize(iter, Spliterator.ORDERED), false);
}

これを使うと、メインの処理はこんなふうに書けます。(ついでに Optional 使って null を隠蔽してみました)

public static String getTreePath4(Node node, String delimiter) {
    List<String> names = iterateLimited(
            Optional.ofNullable(node), o -> o.map(n -> n.getParent()), o -> !o.isPresent())
            .filter(o -> o.isPresent())
            .map(o -> o.get().getName())
            .collect(Collectors.toList());
    Collections.reverse(names);

    return delimiter + String.join(delimiter, names);
}

だいぶ「らしく」なったような気がします。
… iterateLimited みたいな関数は、標準で用意してもらいたいものですが…

(2017/12/06追記)
Java9では簡単に書けるようになりました。
noisyspot.hatenablog.com