git bisectで最初のbadコミットの情報を取得する

最近ようやく git bisectの使い方を学んだ。
ただ、git bisectを使っていて少し躓いたのが、コミットをckeckoutする順番によってはbadコミットの1つ手前でbisectが完了することだ。


具体的には、

A(good) → B(good) → X(bad) → Y(bad) → Z(bad)

という履歴の時に、2分探索が

X → B

という順番で行われる*1ため、bisect の完了時に B がチェックアウトされた状態になる。
この時、一応、badコミットを示す「 is the first bad commit」というメッセージがコンソールに表示されるけど、その後にいろいろコマンドを使っているとあっという間にその出力が流れていってしまう。
そうなった後でgit logとかでなんとかbadコミットの情報を取得しようとしても、簡単にbadコミットの情報が得られない*2



今までは出力結果が流されないように別コンソールでgit bisectをやっていたりしたけど、よくよく調べると最初のbadコミットは「bisect/bad」で参照できるようだ。
bisect/badはgit bisect resetするまでは普通の軽量タグと同じように扱える気がする。

badコミットの内容を表示する git show bisect/bad
badコミットにタグを付ける git tag タグ名 bisect/bad
badコミットを別ブランチに切りだしておく git branch ブランチ名 bisect/bad
badコミットのsha1ハッシュを取得する git rev-parse bisect/bad

等々


注意点としては、あくまでbisect/badは「その時点で分かっている最初のbadコミット」を表すという点だ。
なので、上記の例だと、git bisect開始直後はbisect/badはZを指している。
この点と、git bisect resetすると参照できなくなる点に気をつければ、簡単にbadコミットの情報を利用できるようになると思う。

*1:A と Z の中間のコミット(X)からbisectが開始される

*2:git rev-listとかを使えばおそらく取得はできると思うけど、面倒

コミットメッセージ中に diff を表示したらいろいろ捗った

変更をコミットする時に、大抵の人は git diff --cached なんかを使ってコミット内容を確認してからコミットすると思う*1
ただ、この「コミット前に git diff --cached」作戦は、少なくとも

  • commit --amend の時
  • squash する時

にはうまく適用できない*2


これではちょっと不便なので、コンソールから diff を確認できないならコミットメッセージ中に表示させたらいいじゃない、というわけでやってみた。



イメージとしてはこんな感じ。


デフォルトの(何もしていない)コミットメッセージ:

(えーと、pangram.txt の何を修正したんだっけ?)
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch testing
# Changes to be committed:
#   (use "git reset HEAD ..." to unstage)
#
#       modified:   pangram.txt
#

diff を表示させたコミットメッセージ:

(そうそう、pangram.txt の typo を修正したんだった)
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch testing
# Changes to be committed:
#   (use "git reset HEAD ..." to unstage)
#
#       modified:   pangram.txt
#
#
# diff --git a/pangram.txt b/pangram.txt
# index 3b8357c..84102df 100644
# --- a/pangram.txt
# +++ b/pangram.txt
# @@ -1 +1 @@
# -The quick brwon fox jumps over the lazy dog
# +The quick brown fox jumps over the lazy dog


やり方は簡単、 git フック (prepare-commit-msg) を仕込むだけ。


prepare-commit-msg

#!/bin/sh

case "$2,$3" in
    commit,HEAD)
        echo "# " >> "$1"
        git diff --cached HEAD^ | sed -e 's/^/# /g' >> "$1"
        ;;
    ,|template,|squash,)
        echo "# " >> "$1"
        git diff --cached | sed -e 's/^/# /g' >> "$1"
        ;;
    *)
        ;;
esac

これを .git/hooks において、必要に応じて実行権限を付ければ完了。

ただし、-c または -C オプションを指定してコミットした場合、実際のコミットメッセージに diff の内容が含まれてしまうので要注意。



このフックスクリプトgithub においてる他、前回 の git-hooks スクリプトでもインストールできるので、お好きにどうぞ。

*1:特に git add -p を使った時なんかは

*2:実際にやろうと思ったら、一旦コミットをキャンセルして変更を確認後、再度コミットすればいけるかと

Redmine用Gitフックスクリプトをちょっとだけ汎用的に

TiDD(チケット駆動開発)するにあたって、id:bleis-tiftが非常に便利なフックスクリプトを作ってくれている。
詳しくは Git+Redmineな人におすすめのフックスクリプト集 - みずぴー日記 が分りやすい。


ただ、トピックブランチ名のルール (id/*) や、追加されるメッセージの形式 (refs #*) が決まってて少し使いづらいところもある。


かといって、こんな環境固有の設定をハードコーディングするのもアレだし、設定ファイルにしたところでやっぱりそれをリポジトリに含めるのは微妙だよなぁ…、というわけで、設定ファイルがなければ git-config を使えばいいじゃない、という発想でちょっと改良してみた。
ついでにインストール用スクリプト (git-hooks)*1 も作ったのでインストールが手軽になるはず。


改良点
  • トピックブランチ名、追加メッセージの形式を任意に設定可
  • インストール用スクリプト完備(?) *2
  • ついでに bash-completion 付き
使い方
  1. git-hooks をダウンロードし、パスの通っているフォルダに置く。(他のgitスクリプトがあるフォルダがおそらくベター)
  2. フックスクリプトをインストールしたいリポジトリの中で下記コマンドを実行する。 (要 curl)
$ git hooks install https://github.com/murank/Git-Hooks/raw/master

curl の「SSL certificate problem」がでる場合、「http.sslVerify」 を 「false」 にする (git config --global http.sslVerify false) か、CA証明書を自分でインストールする。
(参考:CygwinでSSLがエラーになる対応に、ルート証明書のアップデートが書いてあった - ブックマクロ開発に)


毎回引数に URL 指定するのが面倒な場合、git-config の hook.remoteURL に指定しておくと引数を省略できる。

$ git config --global hook.remoteURL https://github.com/murank/Git-Hooks/raw/master

git-hooks 自体のアップデートも

# git hooks update https://github.com/murank/Git-Hooks/raw/master

を実行するだけ。これも同じように hook.remoteURL を指定してやることで引数を省略可。



git-hooks も bash-completion に登録したい場合、git-hooks-completion.bashbash-completion.d/ に入れれば使えるようになるはず (要 git-completion)。
入れる場所は下記フォルダのどれかでいいと思う。

  • /etc/bash-completion.d
  • /usr/local/etc/bash-completion.d
  • ~/bash-completion.d
設定

トピックブランチ名の形式 (デフォルト: 「id/%ID%」)
hook.topicBranchFormat にて指定。ブランチ名中のチケットIDの位置は「%ID%」で指定する。(%ID% の位置にある、1個以上の数字をチケットIDとみなす)
sed でチケットIDを取得しているので正規表現も一部可 (ただし、カッコ'()'は使わない方がいいと思う)


例:「feature/id-%ID%/[a-z]*」の場合、「feature/id-123/work」→123



追加メッセージの形式 (デフォルト: 「refs #%ID%」)
hook.msg4TopicBranch にて指定。hook.topicBranchFormat と同じく、
追加メッセージ中のチケットIDの位置は「%ID%」で指定する。


例:「#%ID%」の場合、チケットID:321→「#321」



本家の rewrite 版の開発が結構進んでて今更感はあるけど、一応ソースコードgithub に置いてます。*3

*1:名前かぶったorz https://github.com/bleis-tift/Git-Hooks/tree/rewrite

*2:設定すれば git hooks install でフックスクリプトをインストール可

*3:誰か適当な英語 README を直して…

文字化け判定表を作ってみた

複数の文字コードが出てくるプログラムを書いていると、よく文字化けに遭遇するわけだけど、その度に元の文字コードを調べるのが結構めんどくさい。
というわけで、文章を入力するだけでリアルタイムに一覧を表示してくれる文字化け判定表を作ってみたよ。


文字化け判定表


Javascript での文字コード変換に Escape Codec Library(ecl.js) を使わせてもらっているんだけど*1、このライブラリはデコード時に不正な文字コードをそのまま放置してしまうので、この変換表では不正な文字コードを自前で '?' や '�' に置き換えている。
なので、厳密(?)な変換をした場合と比べると '?' や '�' の数が違ってしまっているけど、あまり気にしない方向で。

*1:実際には ecl.js の高速版 ecl_new.js

Google App Engine を使ってみよう(2) 〜 セッションを使う

前回作った掲示板では投稿後にすぐに一覧画面にリダイレクトするため、投稿完了メッセージを表示できなかった。
POST 後にメッセージを表示するための方法としては、セッションを使う方法がある。

というわけで、今回はセッションを使ってみる。

続きを読む

Google App Engine を使ってみよう(1) 〜 掲示板を作る

今更ながら、Google App Engine に手を出してみた。
webサービスの入門といえば掲示板だろう、ということでまずは掲示板を作ってみる。
とりあえず最新版ということで、SDK は 1.4.3 、slim3 は 1.0.10 を使うことに。

続きを読む

git reset --hard でファイルの変更は取り消せない

ファイルの変更を取り消そうと思って

$ git reset --hard file

としたら

fatal: Cannot do hard reset with paths.

と怒られた。

$ git reset --hard HEAD file

とか

$ git reset --hard HEAD -- file

とか試してもやっぱり怒られる。


じゃあどうすればファイルの変更を取り消せるのかというと

$ git checkout file

とすれば良いみたい。



なんで reset では取り消せないのか調べてみたところ、

git reset [-q] [] [--]
git reset [--patch|-p] [] [--] […]
git reset [--soft | --mixed | --hard | --merge | --keep] [-q] []

git-reset(1)

とあった。
つまり、--hard を指定する場合、引数には しか指定できない。


一方、checkout の場合、

git checkout [-q] [-f] [-m] []
git checkout [-q] [-f] [-m] [--detach] []
git checkout [-q] [-f] [-m] [[-b|-B|--orphan] ] []
git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [] [--]
git checkout [-p|--patch] [] [--] […]

git-checkout(1)

とのことで、こちらは問題なくファイルを指定できる。


checkout なんてブランチの切り替えくらいにしか使ってなかったけど、
なかなか git も奥が深い。