Mobile Factory Tech Blog

技術好きな方へ!モバイルファクトリーのエンジニアたちが楽しい技術話をお届けします!

gitを少し快適に扱うためのワンライナー3選

こんにちは、20卒エンジニアのthe96です。

弊社では、リモート下においても勉強会が日頃から開催されています。 以前、勉強会で同期のワンライナーのプロからawkを授けられて以来、ワンライナーで業務を少し改善することの楽しさに目覚めました。 この記事では、そうして生まれた、gitを使った開発が少し快適になるかもしれないワンライナーを3つ紹介します。

注意

なお、筆者は ripgrep にどっぷりなので、たびたび ripgrep を使うワンライナーが登場します。 ripgrep をインストールするか、適宜 grep に置き換えてください(動作するかは知りません)。

リポジトリ

検証などしたい場合はこちらのリポジトリをご利用ください。 https://github.com/the96/advent-calendar-2021

カレントブランチで変更したファイルのみを対象に検索する

カレントブランチ内で触ったファイルのみを対象にgrepしたいときってありませんか? そういうときにはこのワンライナーをお試しあれ。

git diff --relative --name-status --diff-filter=AM main...HEAD | awk '{print $2}' | xargs -r rg --with-filename test

このワンライナーは、カレントブランチでdiffのあったファイルのみを対象にgrepします。 コマンド中の main の部分をお使いの環境の親ブランチに変更してご利用ください。

f:id:the630:20211221152744p:plain

仕組みは結構シンプルで、前半の git diffawk で差分のあったファイルの名前を抽出して、ripgrep で検索しています。 差分のあったファイルがないブランチで実行したときのために xargs -r を噛ませているのがミソですね。

他にも、未コミットのファイルを対象にしたい場合は main...HEAD--cached に変えてあげると良いでしょう。

git checkoutで切り替えたブランチの履歴を表示する

みなさんはいろんなタスクを並行してこなしているとき、自分がどのブランチで作業していたのか思い出せなくなることはありませんか? そんな時は、このワンライナーをご活用ください。

git branch -a | sed -e 's/..//' | grep -x -f- --color=never <(git reflog | awk '$3~/checkout/{print $8}' | awk '!colname[$1]++{print $1}' | head -n 10)

このワンライナーを使うと、このようにリポジトリ内でチェックアウトしたブランチの履歴を表示することができます。

▼下のスクリーンショットでは使いやすいようにエイリアスを張っています f:id:the630:20211221133715p:plain

削除されたブランチは表示されないようになっています。 f:id:the630:20211221133725p:plain

仕組み

簡単に仕組みを解説します。

git reflogは、gitによるローカルリポジトリの操作履歴などが入っているものです。 チェックアウトの履歴はこのコマンドをもとに取り出しています。 awkでcheckoutのログを探して、headで件数を指定しています。

f:id:the630:20211221133737p:plain
git reflog

そうして取り出した履歴に対して、git branch -a | sed -e 's/..//'で現存するbranch名のみを抽出して、grepを使ってチェックアウト履歴から削除されたブランチを削除しています。

エイリアスの張り方

gitのエイリアスは ~/.gitconfig に記述することで利用できます。 ここで設定したエイリアスは、git ***の *** 部分を直接置換するだけなので、外部コマンドを使う場合は一工夫必要です。

まずは適当な場所にこのワンライナーをシェルスクリプトとして置いておきます。 (ここでは ~/git-checkout-log.sh としておきます)

#!/usr/bin/env zsh
git branch -a | sed -e 's/..//' | grep -x -f- --color=never <(git reflog | awk '$3~/checkout/{print $8}' | awk '!colname[$1]++{print $1}' | head -n 10)

次に、~/.gitconfig 内で下記のように !を頭につけてエイリアスを貼ることで利用できます。

[alias]
    checkout-log = !~/git-checkout-log.sh

参考 2.7 Git の基本 - Git エイリアス

一部のブランチにコミットしないようにする

ブランチ保護ができないprivateリポジトリや、親ブランチなど、直接コミットすることを基本的に避けたいブランチってありますよね。 そういったブランチを保護する方法の一つとして、git hookがあります。

#!/usr/bin/env zsh

readonly PROTECT_BRANCHES=('main' 'release' 'develop')
current_branch=`git rev-parse --abbrev-ref HEAD`

for protect_branch in "${PROTECT_BRANCHES[@]}"
do
    if [ $current_branch = $protect_branch ]; then
        echo "WARNING: You tried commiting to protected branch($protect_branch)!";
        echo "         If you really wanna commit to protected branch, you can use '--no-verify' option."
        exit 1;
    fi
done

このスクリプトを、対象リポジトリ内に .git/hooks/pre-commitとして配置して実行権限を付与すると、対象のブランチにコミットしようとしたときにコミットを止めてくれます。 3行目の readonly PROTECT_BRANCHES=('main' 'release' 'develop')に任意のブランチ名をセットすれば、お好きなブランチを保護することができます。 (もはやワンライナーではないんですが、便利なので紹介したかったんです…)

時には、hotfixなどでどうしても直接コミットしたい時があると思います。 そんなときは no-verify とつけてコミットすると git hook を無視してコミットすることができます。

f:id:the630:20211221133758p:plain

8.3 Git のカスタマイズ - Git フック

終わりに

どれもちょっとしたスクリプトでしたが、みなさんの git 操作が少しでも快適になれば幸いです。

eval "$(hoge init)"するツールをたくさん入れてるシェルの起動速度をちょっと良くする

皆さんのシェルの起動速度はどうですか?シェル起動時に eval "$(hoge init)" を実行するようなツールをたくさん入れていると徐々に遅くなってきてつらいですよね

そこで以下のように hoge init の出力をファイルに書き出しておいて、起動時にはそれをsourceする戦略をとると少しだけシェルの起動を高速化できて少しだけ嬉しいです。

# zshでの例
HOGE_RC_FILE=/path/to/hoge-rc.zsh
[[ ! -e "$HOGE_RC_FILE" ]] && hoge init > "$HOGE_RC_FILE"

source "$HOGE_RC_FILE"

plenv,goenv,nodenv,pyenvを管理しているanyenvでのベンチマークを以下に貼っておきます。

# source/zshrc
ANYENV_RC_FILE=./anyenv-rc.zsh
[[ ! -e "$ANYENV_RC_FILE" ]] && anyenv init - > "$ANYENV_RC_FILE"

source "$ANYENV_RC_FILE"
# eval/zshrc
eval "$(anyenv init -)"
$ hyperfine --shell=zsh --warmup=3 'source $PWD/eval/zshrc' 'source $PWD/source/zshrc'
Benchmark 1: source $PWD/eval/zshrc
  Time (mean ± σ):      1.288 s ±  0.010 s    [User: 0.487 s, System: 0.727 s]
  Range (min … max):    1.278 s …  1.314 s    10 runs

Benchmark 2: source $PWD/source/zshrc
  Time (mean ± σ):     452.8 ms ±   5.7 ms    [User: 189.2 ms, System: 240.1 ms]
  Range (min … max):   446.9 ms … 467.1 ms    10 runs

Summary
  'source $PWD/source/zshrc' ran
    2.85 ± 0.04 times faster than 'source $PWD/eval/zshrc'

この戦略で少し困ることとしては、ツールの更新があるたびにhoge init > $HOGE_RC_FILE相当のコマンドを良きタイミングで実行する必要があることですね。