git stashをもっと便利に扱いたいと思い、fzfを使って使いやすくしてみた。以下のURLに載っているものを参考にして自分にとって使いやすいように改変した。
できたこと
- 今の変更ファイルをfzfを使って選択して、選択したものだけをstash (git-stash-select)
- stash一覧の中から中身をpreviewしながら選び、apply or deleteする (git-stashes)
現在の変更ファイルから一部を選んでgit stashするコマンド
fzfでGUI選択したファイルをgit stashするシェルスクリプト を参考に、git-stash-selectというコマンドを作った。
#!/usr/bin/env bash # Get the root directory of the Git repository git_repo_root=$(git rev-parse --show-toplevel) # Add instructions to the list of modified files instructions="Select multiple files by TAB, and then press Enter to stash them." # Use fzf to select multiple files selected_files=$(git status --porcelain | fzf --multi \ --header="$instructions" \ --preview-window='down:70%' \ --preview=" if [[ {} =~ '^\?\?' ]]; then cat $git_repo_root/{2}; else git -C $git_repo_root diff --color=always {2}; fi " | awk '{ print $2 }' ) # Check if any files were selected if [ -z "$selected_files" ]; then echo "No files selected. Exiting." exit 1 fi # Store the selected files in an array IFS=$'\n' read -rd '' -a files_array <<<"$selected_files" # Filter out the instructions from the array filtered_files_array=() for file in "${files_array[@]}"; do if [[ $file != *"==="* ]]; then filtered_files_array+=("$file") fi done # Prompt for a stash message echo "Enter a stash message:" read stash_message # Stash the selected files git -C $git_repo_root stash push -u -m "$stash_message" -- "${filtered_files_array[@]}"
工夫ポイントはpreviewの部分だ。git diffなどはUntrackedなファイルの内容表示に弱く、そのままだと表示できない。そのためUntrackedな場合はcatを使うなどの場合分けをしている。
stash一覧の中から選んでapply or deleteするコマンド
git-stash-explore を参考に、git-stashesというコマンドを作った。
#!/usr/bin/env zsh while out=$(git stash list "$@" | fzf --ansi --no-sort --reverse --print-query --query="$query" \ --expect=enter,bspace \ --bind="ctrl-space:preview-page-up" \ --bind="space:preview-page-down" \ --bind="k:preview-up" \ --bind="j:preview-down" \ --preview="echo {} | cut -d':' -f1 | xargs -I {STASH} sh -c 'git stash show --color=always -p {STASH}; git show --color=always --format="" -p {STASH}^3'" \ --preview-window='down:85%'); do # Tokenize selection by newline selection=("${(f)out}") # Keep the query accross fzf calls query="$selection[1]" # Represents the key pressed key="$selection[2]" # Represents the stash, e.g. stash{1} reflog_selector=$(echo "$selection[3]" | cut -d ':' -f 1) case "$key" in # enter applies the stash to the current tree enter) git stash apply "$reflog_selector" break ;; # backspace will drop the stash bspace) git stash drop "$reflog_selector" ;; esac done
こちらもUntrackedなファイル対策をしている。In git, is there a way to show untracked stashed files without applying the stash? - Stack Overflow によると、あるstashの中のUntrackedなファイルを表示するには git show 'stash@{0}^3'
のように^3
をつければ良いらしい。そこでgit stash show stash@{0}
とgit show stash@{0}^3
の両方を実行することで、stashの中のModifiedとUntrackedの両方を出力するようにした。
applyとdeleteについては、enterとbackspaceが直感的と感じたので、その2つにキーを割り当てている。
またstashの中身を見るときにpreviewのスクロールをしたい。このコマンドは文字列検索をほぼすることがないなと思ったので、tigなどに合わせてjでscroll down、kでscroll upとなるようにした。
まとめ
今回はgit stashをfzfでもっと便利に扱えるように、変更ファイルを選択してgit stashするコマンドと、stashの中から選択してapplyやdeleteをするコマンドを紹介した。便利なので良かったら使ってほしい。