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コミットを示す「
そうなった後で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コミットの情報を利用できるようになると思う。
コミットメッセージ中に 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 スクリプトでもインストールできるので、お好きにどうぞ。
Redmine用Gitフックスクリプトをちょっとだけ汎用的に
TiDD(チケット駆動開発)するにあたって、id:bleis-tiftが非常に便利なフックスクリプトを作ってくれている。
詳しくは Git+Redmineな人におすすめのフックスクリプト集 - みずぴー日記 が分りやすい。
ただ、トピックブランチ名のルール (id/*) や、追加されるメッセージの形式 (refs #*) が決まってて少し使いづらいところもある。
かといって、こんな環境固有の設定をハードコーディングするのもアレだし、設定ファイルにしたところでやっぱりそれをリポジトリに含めるのは微妙だよなぁ…、というわけで、設定ファイルがなければ git-config を使えばいいじゃない、という発想でちょっと改良してみた。
ついでにインストール用スクリプト (git-hooks)*1 も作ったのでインストールが手軽になるはず。
使い方
- git-hooks をダウンロードし、パスの通っているフォルダに置く。(他のgitスクリプトがあるフォルダがおそらくベター)
- フックスクリプトをインストールしたいリポジトリの中で下記コマンドを実行する。 (要 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.bash を bash-completion.d/ に入れれば使えるようになるはず (要 git-completion)。
入れる場所は下記フォルダのどれかでいいと思う。
設定
トピックブランチ名の形式 (デフォルト: 「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」
*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
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(1)] [--] …
git reset [--patch|-p] [] [--] [ …]
git reset [--soft | --mixed | --hard | --merge | --keep] [-q] []
とあった。
つまり、--hard を指定する場合、引数には
一方、checkout の場合、
git checkout [-q] [-f] [-m] [
git-checkout(1)]
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] [] [--] [ …]
とのことで、こちらは問題なくファイルを指定できる。
checkout なんてブランチの切り替えくらいにしか使ってなかったけど、
なかなか git も奥が深い。