Oh-my-zshのテーマをカスタマイズ

今回は、Oh-my-zshをインストールしthemaをカスタマイズしました。

curl -L https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh | sh
 install.shでは特に難しいことやっていないため、手動で行っても良いかもしれません。

32

早くて簡単です。

.oh-my-zshが新しく作成されます。


.oh-my-zsh
├── MIT-LICENSE.txt
├── README.textile
├── custom
├── lib
├── log
├── oh-my-zsh.sh
├── plugins
├── templates
├── themes
└── tools
今回注目するのは、themesです。
oh-my-zshには多くのプロンプトのテーマが存在します。
Themes

これらのテーマを使うのもいいのですがやはり自分専用の
プロンプトがほしくなると思います。ということで作りました。
今回はプロンプトに表示したかったものとして
 ・Gitのリポジトリの情報
 ・バックグランドのプロセスの数
です。

Gitのリポジトリの情報


Gitの情報の取得の関数はすでに.oh-my-zsh/lib/git.zshにあります。
しかし、すでにある関数では主にファイルの変更、削除やリモートとのコミットの差分のチェックは
存在の有無であったため自分は数まで把握したいなと思いカスタマイズしました。

Gitのリポジトリの情報としてほしかったのが
 ・ローカルリポジトリの状態
 ・リモートリポジトリとの差分

ローカルリポジトリの状態

まず、ローカルのリポジトリの情報の取得です。

以下がコードです。(ほとんど.oh-my-zsh/lib/git.zshと同じかつ汚いコードになりました)
custom_git_prompt_status() {
  #INDEX=$(command git status --porcelain -b 2> /dev/null)
  # for git version 1.7.1
  INDEX=$(command git status --porcelain 2> /dev/null)
  STATUS=""
  if COUNT=$(echo "$INDEX" &> /dev/null | grep -E '^\?\? ' -c ) && [ $COUNT -gt 0 ]; then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_UNTRACKED$COUNT"
  fi

  # Staged
  STAGED=""
  if COUNT=$(echo "$INDEX" &> /dev/null | grep '^A  ' -c ) && [ $COUNT -gt 0 ]; then
    STAGED="$STAGED$ZSH_THEME_GIT_PROMPT_STAGED_ADDED$COUNT"
  fi
  if COUNT=$(echo "$INDEX" &> /dev/null | grep '^M  ' -c ) && [ $COUNT -gt 0 ]; then
    STAGED="$STAGED$ZSH_THEME_GIT_PROMPT_STAGED_MODIFIED$COUNT"
  fi
  if COUNT=$(echo "$INDEX" &> /dev/null | grep '^R  ' -c ) && [ $COUNT -gt 0 ]; then
    STAGED="$STAGED$ZSH_THEME_GIT_PROMPT_STAGED_RENAMED$COUNT"
  fi
  if COUNT=$(echo "$INDEX" &> /dev/null | grep '^D  ' -c ) && [ $COUNT -gt 0 ]; then
    STAGED="$STAGED$ZSH_THEME_GIT_PROMPT_STAGED_DELETED$COUNT"
  fi

  if [ "$STAGED" != "" ];then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_STAGED_PREFIX$STAGED$ZSH_THEME_GIT_PROMPT_STAGED_SUFFIX"
  fi

  # Unstaged
  UNSTAGED=""
  if COUNT=$(echo "$INDEX" &> /dev/null | grep '^AM \|^ T \|^ M ' -c ) && [ $COUNT -gt 0 ]; then
    UNSTAGED="$UNSTAGED$ZSH_THEME_GIT_PROMPT_MODIFIED$COUNT"
  fi
  if COUNT=$(echo "$INDEX" &> /dev/null | grep '^AD ' -c ) && [ $COUNT -gt 0 ]; then
    UNSTAGED="$UNSTAGED$ZSH_THEME_GIT_PROMPT_DELETED$COUNT"
  fi

  if [ "$UNSTAGED" != "" ];then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_UNSTAGED_PREFIX$UNSTAGED$ZSH_THEME_GIT_PROMPT_UNSTAGED_SUFFIX"
  fi

  if $(command git rev-parse --verify refs/stash >/dev/null 2>&1); then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_STASHED"
  fi
  if $(echo "$INDEX" | grep '^UU ' &> /dev/null); then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_UNMERGED"
  fi
  if $(echo "$INDEX" | grep '^## .*ahead' &> /dev/null); then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_AHEAD"
  fi
  if $(echo "$INDEX" | grep '^## .*behind' &> /dev/null); then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_BEHIND"
  fi
  if $(echo "$INDEX" | grep '^## .*diverged' &> /dev/null); then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_DIVERGED"
  fi

  echo $STATUS
}

まずGitの状態を取得するためにgit status --porcelainを使っています。
これは以下のようにステータス情報を簡単に表示してくれます。
$git status --porcelain
 M zsh/theme/ooma.zsh-theme
?? test
custom_git_prompt_statusでは表示された内容をgrepで検索を行い、
存在する行数をカウントして表示するという 簡単なメソッドです。
$git status --porcelain | grep '^ M'
 M zsh/theme/ooma.zsh-theme
$git status --porcelain | grep '^ M' -c
stashのチェックもここで行っています。
  if $(command git rev-parse --verify refs/stash >/dev/null 2>&1); then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_STASHED"
  fi
コマンドが私が見たことの無いrev-parseを使っていました。
おもにコミットのハッシュを表示してくれるコマンドでした。
さらにここで使用しているオプション --verifyは指定されたオブジェクトがGitのオブジェクトかどうかチェックしています。
what-does-git-rev-parse-do

でもなぜ refs/stash と思いませんか?
どこを指定しているのだろうと。
調べたら.git/refs/stash(ファイル)を指していました。
Gitの内側-Gitの参照 ← 詳しくはこちらでお願いします。
$tree .git/refs
.git/refs ├── heads │   ├── linux │   └── master ├── remotes │   └── origin │   ├── HEAD │   ├── linux │   └── master ├── stash └── tags
└── ver1.0
refs/stashにはpushされたときのHEADのコミットハッシュが書かれています。
さらにrefs/stashはstashが存在する時のみに作成されるファイルです。
不思議なことにstashには最新のstash時のみコミットハッシュしか保存されていませんでした。
他のstashの情報はどこにあるのだろうかと思い調べたのですが分からなかったです><
すいません。


リモートリポジトリとの差分

次はリモートとの差分です。
リモートとの差分といっても比較対象はgit fetchしてきたリポジトリの情報になります。
 custom_git_remote_status() {
    remote=${$(command git rev-parse --verify ${hook_com[branch]}@{upstream} --symbolic-full-name 2>/dev/null)/refs\/remotes\/}
    if  -n ${remote}  ; then
        ahead=$(command git rev-list ${hook_com[branch]}@{upstream}..HEAD 2>/dev/null | wc -l)
        behind=$(command git rev-list HEAD..${hook_com[branch]}@{upstream} 2>/dev/null | wc -l)

        if [ $ahead -eq 0 ] && [ $behind -gt 0 ]
        then
            echo "$ZSH_THEME_GIT_PROMPT_BEHIND_REMOTE"
        elif [ $ahead -gt 0 ] && [ $behind -eq 0 ]
        then
            echo "$ZSH_THEME_GIT_PROMPT_AHEAD_REMOTE"
        elif [ $ahead -gt 0 ] && [ $behind -gt 0 ]
        then
            echo "$ZSH_THEME_GIT_PROMPT_DIVERGED_REMOTE"
        fi
    fi
}
ここでも出ました。rev-parse --verify です。
${hook_com[branch]}にはブランチ名が入ってきます。
--symbolic-full-nameはrev-parseで出力するときにGitのオブジェクトのパスを渡します。

通常の場合・・コミットハッシュのみ
$git rev-parse --verify master
30c1615daa638a7442c2ab043c9ba7021ba81c3f
--symbolicの場合 ブランチ名(リモートの場合はリモート名も付く)
$git rev-parse --verify master --symbolic
master
--symbolic-full-nameの場合 (gitオブジェクトのパスが表示される)
$git rev-parse --verify master --symbolic-full-name
refs/heads/master
おおこれでローカルのgitのオブジェクトのパスが手に入りました。
でもほしいのはリモートです。そこで@{upstream}の出番です。git-rev-parse
@{upstream}は、指定されたブランチのupstreamのgitのオブジェクトのパスを取得してくれます。

$git rev-parse --verify master@{upstream} --symbolic-full-name
refs/remotes/origin/master
upstreamとは該当するブランチの本家のリポジトリ?みたいな感じで言い方が悪いかもしれません。
(リモートのブランチのどれかに該当します)definition-of-downstream-and-upstream

git branch -vv(vが二つ)で見ることが出来ます。下の実行結果ですとmasterブランチの
upstreamは「origin/master」となります。
how-do-i-get-git-to-show-me-which-branches-are-tracking-what

$git branch -vv
  linux  df0b61f remove tmp
* master 59a593a [origin/master: ahead 1] delete space

rev-listについて git-rev-list 
-- list commit object in reverse chronological order
新しい順にコミットハッシュを並べてくれます。
これによりリモートとのコミットの差分が存在するのかチェックしてくれます。
$git rev-list HEAD..refs/remotes/origin/master
471fff9b92650c5e4deaec584a3b6f62ad1543a6
あとはコミットハッシュの数をカウントすれば差分の数が分かります。

バックグラウンドのジョブ数確認

頑張ってコードを書いていました。
ZSH_THEME_BACKGROUND_JOB_PREFIX="%{$C%}"
ZSH_THEME_BACKGROUND_JOB_SUFFIX="%{$RE%}"

background_jobs() {
  jobs=$(jobs 2>/dev/null)
  if [ $jobs ];then
    echo "$ZSH_THEME_BACKGROUND_JOB_PREFIX"
    for i in $jobs; do
      echo $i
    done
    echo "$ZSH_THEME_BACKGROUND_JOB_SUFFIX"
  fi
}

でも zsh-prompt-checking-if-there-are-any-background-jobsを見て
「%j」で代用できることを知り開発をやめました。
バックグラウンドの種類まで表示しようとしましたが出来ませんでした。

完成形
通常
35

git 
48


色々いじった状態 ??←unstaged  [緑] ←インデックスの状態  [黄色]←作業ディレクトリの状態
05


感想
コードが汚い、きれいにする。バックグランドの種類までほしいので頑張る。
(周りにはそこまでしなくてもと言われたが)
カスタマイズはやっぱり面白い。Gitの知らないことが分かってよかった。
githubのコードです