gitで変更のあったファイルを簡単に開ける git-edit について
とにかく目的のファイルにすぐにアクセスしたかった。
ただそれだけ。
コードはGithubにあります。
github - yonchu/git-edit
インストール方法:
- git-edit をPATHの通った所に置くだけ
- zshを使用している場合は _git-edit をfpathの通った所に置いて下さい。
(※ fpath は echo "$fpath" などで確認して下さい)
どういうコマンドなのか
変更のあったファイルをエディタで開くための gitサブコマンドです。
コマンド形式は以下
$ git edit [commit] [file ...]
こんな感じで使います。
$ git edit $ git edit HEAD $ git edit hoge $ git edit * $ git edit *.txt $ git edit HEAD^ dir/*
コミットの指定はハッシュ値でも構いません。
zshの補完機能との連携
今回一番やりたかったこと。
git-edit単体でも、まあそれなりに使えますがzshと併用することで格段に使い勝手がよくなる…と思います。たぶん
こんな感じで変更のあったファイル名が補完できます。
$ git edit <TAB> Editable Files xxxx.txt yyyy.sh zzzz.c Deleted Files hoge.txt $ git edit HEAD <TAB> Commit Hash:62c91ef1f56d89b3328fa4aeae4bb52b579ad679 Editable Files aaaa.txt bbbb.sh cccc.c Deleted Files fuga.txt
zshの設定について
zshの設定によっては上記とは違った表示になるかも。
補完候補の一覧表示はデフォルトでも有効になっているはずだけど、説明項目(ラベル)はデフォルトでは表示されないはず。
説明項目(ラベル)を表示させるには "~/.zshrc" 等に以下のように設定します。
zstyle ':completion:*' group-name '' zstyle ':completion:*:descriptions' format '%B%d%b'
%B はボールド(太字)の有効、%b はボールドの無効 (不要なら消して下さい)
%F{color name} と %f で色を変更することもできます。
%d が説明項目(ラベル)の指定
注意事項
- submodule の変更は無視されます。そこまではちょっと…
- ファイル名をリネームした場合はリネーム前のファイル名が Deleted FIles に表示されます
- HEADなどのコミットオブジェクトは補完されません。これはそのうち対応するかも
以上
ちょっと説明がざっくりし過ぎているが、そんな需要があるとも思えないのでよしとしよう。
bash/zshで16色(ANSI カラーコード)と256色のカラーパレットを表示
なんだか定期的に色を変えたくなる…
プロンプトの色やtmux/screenのステータスバーの色変更時のお共に。
yonchu/shell-color-pallet · GitHub
256色カラーパレットを表示
こんな感じに表示されます。
【Mac】zshをサブシェルで起動するとPATHがおかしくなる
どういう状態かというと。
★サブシェル起動前 $ printpath ← $PATHを表示 /Users/yonchu/.pythonbrew/bin /Users/yonchu/.rvm/gems/ruby-1.9.3-p286/bin /Users/yonchu/.rvm/gems/ruby-1.9.3-p286@global/bin /Users/yonchu/.rvm/rubies/ruby-1.9.3-p286/bin /Users/yonchu/.rvm/bin /usr/local/share/python /usr/local/mysql/bin /Library/Java/Home/bin /Users/yonchu/bin /Users/yonchu/dotfiles/bin /usr/local/bin /usr/local/sbin /usr/local/share /usr/bin /bin /usr/sbin /sbin /usr/X11/bin $ $ zsh ← サブシェルとして zsh を起動 $ ★サブシェル起動後 $ printpath ← $PATHを表示 /Users/yonchu/.pythonbrew/bin /Users/yonchu/.rvm/gems/ruby-1.9.3-p286/bin /Users/yonchu/.rvm/gems/ruby-1.9.3-p286@global/bin /Users/yonchu/.rvm/rubies/ruby-1.9.3-p286/bin /Users/yonchu/.rvm/bin /usr/bin /bin /usr/sbin /sbin /usr/local/bin /usr/X11/bin /usr/local/share/python /usr/local/mysql/bin /Library/Java/Home/bin /Users/yonchu/bin /Users/yonchu/dotfiles/bin /usr/local/sbin /usr/local/share
(※ printpathはPATHを整形して出力してるだけです)
という感じです。
意味分かりませんね。
原因
何が問題なのかというと、/etc/zshenv に致命的な誤りがありました。
普段ユーザが触れるようなファイルではないので、え?と思うかもしれません。
この問題の原因に対して、homebrewのzshで以下のような注意書きがありました。
$ brew info zsh zsh: stable 5.0.0 http://www.zsh.org/ Depends on: gdbm, pcre /usr/local/Cellar/zsh/5.0.0 (956 files, 9.1M) * https://github.com/mxcl/homebrew/commits/master/Library/Formula/zsh.rb ==> Options --disable-etcdir Disable the reading of Zsh rc files in /etc ==> Caveats To use this build of Zsh as your login shell, add it to /etc/shells. If you have administrator privileges, you must fix an Apple miss configuration in Mac OS X 10.7 Lion by renaming /etc/zshenv to /etc/zprofile, or Zsh will have the wrong PATH when executed non-interactively by scripts. Alternatively, install Zsh with /etc disabled: brew install --disable-etcdir zsh
homebrewでzshを入れている方はこの注意書きに気づいたでしょうか。
自分は全然気づきませんでした…
対処方法
説明に書かれている通り、対処方法は2種類
今回私は1を採用しました。
(1の方が今後気にすることが少なそうなので)
$ ls -l /etc/zshenv -r--r--r-- 1 root wheel 126 2012-04-06 03:56 zshenv $ sudo mv /etc/zshenv /etc/zprofile $ ls -l /etc/zprofile -r--r--r-- 1 root wheel 126 2012-04-06 03:56 zprofile
以下、自分用のメモを兼ねて説明
まず、zsh起動時に読み込まれる設定ファイルの読み込み順序についてです。
zshの設定ファイルは種類が多いので非常に分かりづらいです。
zshを起動する方法によって以下のように分けられます。
ログインシェルとして起動した場合
$ZDOTDIR/.zshenv
/etc/zprofile
$ZDOTDIR/.zprofile
/etc/zshrc
$ZDOTDIR/.zshrc
/etc/zlogin
$ZDOTDIRA/.zlogin
インタラクティブシェル(対話シェル)として起動した場合
(GUIターミナル/ssh/サブシェルなど)
(※ MacのGUIターミナルはログインシェル扱いみたいです)
$ZDOTDIR/.zshenv
/etc/zshrc
$ZDOTDIR/.zshrc
ノンインタラクティブシェル(非対話シェル)として起動した場合
(シェルスクリプト/リモートシェルなど)
$ZDOTDIR/.zshenv
※ $ZDOTDIRを設定していない場合は、$HOMEになります。
今回問題となったのはサブシェルなので2番目のケースですね。
続いて問題の /etc/zshenv の中身です。
$ cat /etc/zshenv # system-wide environment settings for zsh(1) if [ -x /usr/libexec/path_helper ]; then eval `/usr/libexec/path_helper -s` fi
path_helper というコマンドを起動しているだけのようです。
path_helper というのは man によると、
DESCRIPTION The path_helper utility reads the contents of the files in the directories /etc/paths.d and /etc/manpaths.d and appends their contents to the PATH and MANPATH environment variables respectively. (The MANPATH environment variable will not be modified unless it is already set in the environment.) Files in these directories should contain one path element per line. Prior to reading these directories, default PATH and MANPATH values are obtained from the files /etc/paths and /etc/manpaths respectively. Options: -c Generate C-shell commands on stdout. This is the default if SHELL ends with "csh". -s Generate Bourne shell commands on stdout. This is the default if SHELL does not end with "csh".
ということだそうです。
要するにデフォルトのPATHを設定しますってことですね。
デフォルトで設定されるパスは、/etc/paths に書かれているパスと /etc/paths.d 配下にあるファイルの中に書かれているパスです。
私のMac(OS X 10.7)では以下のようなパスが書かれていました。
$ cat /etc/paths /usr/bin /bin /usr/sbin /sbin /usr/local/bin $ cat /etc/paths.d/* /usr/X11/bin
ここで私のPATH設定環境について補足しておくと、
基本的に、PATHは ZDOTDIR/.zprofile で設定しています。
しかし、一部 pythonbrew や rvm などのPATH設定処理を含んでいるスクリプトを $ZDOTDIR/.zshrc で読み込んでいます。
サブシェル起動時のPATH設定の流れ
以上を踏まえて、サブシェル起動時にどのようにPATHが設定されているかを考えてみます。
おそらく以下のような流れになっていると思われます。
- /etc/zshenv がデフォルトパスを設定
- 親シェルで元々設定されていたPATHを設定(環境変数の引き継ぎ)
- ZDOTDIR/.zshrc に書かれているPATHを設定
ここで1つ注意すべき点があります。
PATHを設定する際、重複したパスが存在すると"左"に書かれているパスが優先して設定され、"右"にあるパスは削除されます。
この重複パスを削除する機能は、通常 "typeset -U path" を行なっていないと有効になりませんが、2番目の親シェルから引き継いだパスを設定する際は、"typeset -U" を行なっていなくても重複パスが削除されるようになっています。
この重複削除によって、一見なぜあんな並び方になったのかが分かりづらい状態になってしまったわけです。
また、.zprofile や .zlogin を使用せず、PATH設定を全て .zshrc で行なっている場合は、サブシェル起動時でも .zshrc でPATHを上書きできるため問題が表面化しづらくなっています。
しかし、/etc/zshenv はサブシェルなどのインタラクティブシェルだけでなく、通常のスクリプトをzshで起動した場合にも読み込まれてしまうため、問題になる可能性があります。
(シェバングでzshを指定している場合や、zshを指定してスクリプトを起動する場合など)
Macでzshを使用している方はお気をつけ下さい。
zshのchpwdの設定
今回もzshネタです。
zshではchpwd関数を使用してcd後に自動でlsを実行するというのはもはやお馴染みなわけですが、移動先のディレクトリ配下のファイル数があまりにも多いと、画面から溢れて残念な結果になったりします。
そこでchpwdを以下のようにすることでファイル数が多い場合にも良い感で表示されるようになります。
これでファイル数が多い場合は以下のように表示されるようになります。
[yonchu@macbook:~]$ cd /usr/bin ./ dprofpp5.12* jcmd@ pg_config* snmpdf* ../ dropdb* jconsole@ pg_dump* snmpget* 2to3* droplang* jdb@ pg_dumpall* snmpgetnext* 2to3-* dropuser* jhat@ pg_restore* snmpinform* 2to3-2.7@ drutil* jinfo@ pg_upgrade* snmpnetstat* ... dispqlen.d* javadoc@ perlthanks* snmp-bridge-mib* zipsplit* ditto* javah@ perlthanks5.10* snmpbulkget* zless* dns-sd* javap@ perlthanks5.12* snmpbulkwalk* zmore* dprofpp* javatool* pfbtops* snmpconf* znew* dprofpp5.10* javaws@ pg_basebackup* snmpdelta* zprint* 1098 files exist [yonchu@macbook:/usr/bin]$
OSの種類に応じて実行するlsコマンドを変更したりもしています。
また、Macの場合はglsをインストールすることをオススメします。*1
―――
上記の設定は私のgithub(yonchu/dotfiles)にて公開しています。
良かったら参考にして下さい。
zshのプロンプトにgitの状態表示 - 未add/未commit/未push/untracked(未追跡)に対応
zshのプロンプトにgitの状態(add/commit/push)を表示する方法に関しては、こことここを参考にしています。
今回はそこにプラスしてUntracked(未追跡)なファイルが存在しているかどうかも分かるようにzshのプロンプトに表示させます。
さらに状態を表す記号は全てブランチ名の”前”に付けるようにしました。
その他、git init直後などでHEADがまだ存在しない場合にも正常に表示されるようにするための対応、リモートリポジトリが存在しない場合に未pushと認識しないように対応したました。
っでこんな感じになりました。
.zshrcに設定する内容はこんな感じ
### zshプロンプト設定 # カラーの設定を$fg[red]のように人がわかるような書き方ができる autoload -Uz colors colors # # Color定義(あとで変更しやすいように) # DEFAULT=$'%{\e[0;0m%}' RESET="%{${reset_color}%}" GREEN="%{${fg[green]}%}" BOLD_GREEN="%{${fg_bold[green]}%}" BLUE="%{${fg[blue]}%}" BOLD_BLUE="%{${fg_bold[blue]}%}" RED="%{${fg[red]}%}" BOLD_RED="%{${fg_bold[red]}%}" CYAN="%{${fg[cyan]}%}" BOLD_CYAN="%{${fg_bold[cyan]}%}" YELLOW="%{${fg[yellow]}%}" BOLD_YELLOW="%{${fg_bold[yellow]}%}" MAGENTA="%{${fg[magenta]}%}" BOLD_MAGENTA="%{${fg_bold[magenta]}%}" WHITE="%{${fg[white]}%}" setopt prompt_subst autoload -Uz add-zsh-hook # # Gitの状態表示 # # 記号について # - : WorkingTreeに変更がある場合(Indexにaddしていない変更がある場合) # + : Indexに変更がある場合(commitしていない変更がIndexにある場合) # ? : Untrackedなファイルがある場合 # * : remoteにpushしていない場合 # autoload -Uz vcs_info zstyle ':vcs_info:*' enable git svn hg bzr zstyle ':vcs_info:*' formats '(%s)-[%b]' zstyle ':vcs_info:*' actionformats '(%s)-[%b|%a]' zstyle ':vcs_info:(svn|bzr):*' branchformat '%b:r%r' zstyle ':vcs_info:bzr:*' use-simple true autoload -Uz is-at-least if is-at-least 4.3.10; then # zshが4.3.10以上の場合 zstyle ':vcs_info:git:*' check-for-changes true zstyle ':vcs_info:git:*' stagedstr "+" zstyle ':vcs_info:git:*' unstagedstr "-" zstyle ':vcs_info:git:*' formats '%s,%u%c,%b' zstyle ':vcs_info:git:*' actionformats '%s,%u%c,%b|%a' fi function _update_vcs_info_msg() { psvar=() LANG=en_US.UTF-8 vcs_info local _vcs_name _status _branch_action if [ -n "$vcs_info_msg_0_" ]; then _vcs_name=$(echo "$vcs_info_msg_0_" | cut -d , -f 1) _status=$(_git_untracked_or_not_pushed $(echo "$vcs_info_msg_0_" | cut -d , -f 2)) _branch_action=$(echo "$vcs_info_msg_0_" | cut -d , -f 3) psvar[1]="(${_vcs_name})-[${_status}${_branch_action}]" fi # 右側プロンプト RPROMPT="${RESET}%1(v|${RED}%1v|)${RESET}${BOLD_YELLOW}${VIRTUAL_ENV:+($(basename "$VIRTUAL_ENV"))}${RESET}[${MAGENTA}%D{%Y/%m/%d %H:%M:%S}${RESET}]${RESET}" } add-zsh-hook precmd _update_vcs_info_msg # # Untrackedなファイルが存在するか未PUSHなら記号を出力 # Untracked: ? # 未PUSH: * # function _git_untracked_or_not_pushed() { local git_status head remotes stagedstr local staged_unstaged=$1 not_pushed="*" untracked="?" # カレントがgitレポジトリ下かどうか判定 if [ "$(git rev-parse --is-inside-work-tree 2> /dev/null)" = "true" ]; then # statusをシンプル表示で取得 git_status=$(git status -s 2> /dev/null) # git status -s の先頭が??で始まる行がない場合、Untrackedなファイルは存在しない if ! echo "$git_status" | grep "^??" > /dev/null 2>&1; then untracked="" fi # stagedstrを取得 zstyle -s ":vcs_info:git:*" stagedstr stagedstr # git status -s の先頭がAで始まる行があればstagedと判断 if [ -n "$stagedstr" ] && ! printf "$staged_unstaged" | grep "$stagedstr" > /dev/null 2>&1 \ && echo "$git_status" | grep "^A" > /dev/null 2>&1; then staged_unstaged=${staged_unstaged}${stagedstr} fi # HEADのハッシュ値を取得 # --verify 有効なオブジェクト名として使用できるかを検証 # --quiet --verifyと共に使用し、無効なオブジェクトが指定された場合でもエラーメッセージを出さない # そのかわり終了ステータスを0以外にする head=$(git rev-parse --verify -q HEAD 2> /dev/null) if [ $? -eq 0 ]; then # HEADのハッシュ値取得に成功 # リモートのハッシュ値を配列で取得 remotes=($(git rev-parse --remotes 2> /dev/null)) if [ "$remotes[*]" ]; then # リモートのハッシュ値取得に成功(リモートが存在する) for x in ${remotes[@]}; do # リモートとHEADのハッシュ値が一致するか判定 if [ "$head" = "$x" ]; then # 一致した場合はPUSH済み not_pushed="" break fi done else # リモートが存在しない場合 not_pushed="" fi else # HEADが存在しない場合(init直後など) not_pushed="" fi # Untrackedなファイルが存在するか未PUSHなら記号を出力 if [ -n "$staged_unstaged" -o -n "$untracked" -o -n "$not_pushed" ]; then printf "${staged_unstaged}${untracked}${not_pushed}" fi fi return 0 }
状態を表す記号は以下のようになっています。
- : WorkingTree変更がある場合(Indexにaddしていない変更がある場合) + : Indexに変更がある場合(commitしていない変更がIndexにある場合) ? : Untrackedなファイルがある場合 * : remoteにpushしていない場合(リモートが設定されている場合のみ)
記号を変更したい方は
zstyle ':vcs_info:git:*' stagedstr "+" zstyle ':vcs_info:git:*' unstagedstr "-" local staged_unstaged=$1 not_pushed="*" untracked="?"
の行の記号を変更して下さい。
記号の出力位置とかが気に食わないよって方は
psvar[1]="(${_vcs_name})-[${_status}${_branch_action}]"
の部分を修正して下さい。
上記の設定は私のgithub(yonchu/dotfiles)にて公開しています。
良かったら見てみたください。