git status, git stash list, git logを同時に出力するサブコマンドを作ったらすごい捗った話

# 経緯
最近忘れっぽいのか、stashしたままほっぽったり、pushするの忘れて帰ったりってことをよくしてしまいます。
特にstashなんかはたまにしか使わないため、``git stash list``して、保存したstashのリストをチェックする、なんて体が覚えてくれません。

# そんなわけで……
``git localinfo`` というサブコマンドを作ってみました。
実行すると、ローカルリポジトリでの変更内容が以下のようにコンパクトに出力されます。

![shell_prompt.png](https://qiita-image-store.s3.amazonaws.com/0/45372/7e849313-1bb7-8831-df21-d7291f5a0d05.png)

出力されるのは、

* 現在のブランチ名（1行目のパスの末尾）
* unstaged, stagedなファイルの一覧
* stash一覧
* 未pushのコミット一覧

です。何も変更がなければパス＆ブランチ名も出力しません。
これを、``git status``の代わりに叩くように癖をつけたら[^1]、stashしたままほっぽったり、push忘れて帰ったり、ってことが無くなりました。

[^1]: 私は、``alias gs="git localinfo"`` と ``alias gsall='gs `ghq list -p`'`` を定義してます。何かするたびに``gs``を叩くように手が覚えました^^

# もうちょっと進んだ使い方
引数に、``git log`` と ``git stash list`` の両方で有効なオプションを指定できます。``--pretty``で出力内容を調節したり、``--name-status``で変更ファイル名も出力したりするのに使ってます。

![shell_prompt.png](https://qiita-image-store.s3.amazonaws.com/0/45372/b94b7600-4212-e0ab-362e-be176ae6ecf8.png)

また、引数に複数のパスを指定することで、複数のローカルリポジトリの内容を出力できます。

```shell-session
git localinfo `ghq list -p`
```

とかすると全ての作業中の内容が表示されるので、複数のリポジトリを行ったり来たりしていてもpush漏れが無くなりました。

# 作成したもの
[GitHubにも置いてますが](https://github.com/momo-lab/dotfiles/blob/master/.myscript/bin/git-localinfo)、私のdotfilesの一部としてなので、ソースをそのまま貼っておきます。

```bash:git-localinfo
#!/bin/bash

function show_git_status() {
  path=$1
  shift
  status=`
    cd $path
    if [ -n "$(git rev-parse --git-dir 2> /dev/null)" ]; then
      git -c color.ui=always status -s;
      git -c color.ui=always stash list --oneline --decorate $*;
      git -c color.ui=always log --branches --not --remotes --oneline --graph --decorate $*
    fi
  `
  if [ "$status" != "" ]; then
    if [ "$path" != "" ]; then
      branch=`cd $path; git symbolic-ref --short HEAD`
      echo -e "\e[92m$path\e[0m (\e[32m$branch\e[0m)"
    fi
    echo "$status"
  fi
}

# parse options
declare -a argv=()
declare -a options=()
while [ $# -gt 0 ]; do
  case "$1" in
    -*) options=("${options[@]}" "$1");;
    *)  argv=("${argv[@]}" "$1");;
  esac
  shift
done
if [ ${#argv[@]} -eq 0 ]; then
  argv=(`pwd`)
fi

# show git status
for path in ${argv[@]}; do
  show_git_status $path ${options[@]}
done
```

# 知ったこと
このサブコマンドを作成する際に知ったオプションを紹介します。

## ``-c color.ui=always``
``git status`` などを行った結果、出力があるかどうか(変更があるかどうか)を知るために、いったん変数に入れているのですが、そうすると色情報が出力されません。
``git log``や``git status list``は``--color``オプションで強制的に色情報を出力できるのですが、``git status``には同様のオプションがないため、``-c``オプションを使用して一時的にconfigを切り替えています。

## ``git log --not --remotes``
リモートリポジトリ(の、origin)にpushしていないコミットのみを表示します。
``--not``を付け、次のオプションで指定しているコミットの意味を反転させることで表示しています。

# 終わりに
``git log --help`` するといろいろなオプションがあるのがわかるから一度読むとよいよ。

# 2016-11-24 追記
ブランチ名の取得時にcdしていなくて、引数を指定していても常に現在のディレクトリのブランチ名を出力するようになってしまっていました。また、git管理下ではないディレクトリを指定して実行するとエラーメッセージが出てしまっていました。
これらを修正しましたので、使ってくれている方は以下のように修正をお願いします。

```diff
index 8cd58bb..eb014ea 100755
--- a/.myscript/bin/git-localinfo
+++ b/.myscript/bin/git-localinfo
@@ -5,15 +5,13 @@ function show_git_status() {
   shift
   status=`
     cd $path
-    if [ -n "$(git rev-parse --git-dir 2> /dev/null)" ]; then
-      git -c color.ui=always status -s;
-      git -c color.ui=always stash list --oneline --decorate $*;
-      git -c color.ui=always log --branches --not --remotes --oneline --graph --decorate $*
-    fi
+    git -c color.ui=always status -s;
+    git -c color.ui=always stash list --oneline --decorate $*;
+    git -c color.ui=always log --branches --not --remotes --oneline --graph --decorate $*
   `
   if [ "$status" != "" ]; then
     if [ "$path" != "" ]; then
-      branch=`cd $path; git symbolic-ref --short HEAD`
+      branch=`git symbolic-ref --short HEAD`
       echo -e "\e[92m$path\e[0m (\e[32m$branch\e[0m)"
     fi
     echo "$status"
```