SOTONO / ZSHCMDS

SOTONO / DOTFILES / ZSH

01 / 06

repo

移動 / 選択zsh/repo.zsh依存: fzf

ABOUT

fzf でリポジトリを選んで一撃で cd する。worktree があればブランチまで選べる。

WORK_DIR(既定: ~/Documents)配下を最大 4 階層まで走査して Git リポジトリを列挙し、fzf で選んで cd する。候補は TTL 付き(既定 300 秒)でキャッシュされるので 2 回目以降は即座に開く。選んだリポジトリに複数の worktree がある場合は第 2 段階としてブランチ選択に進む。クエリを渡すと部分一致で絞り込み、1 件だけなら fzf を省略して直行する。移動履歴は ~/.repo_history に追記される。

USAGE

repo [--refresh] [work_dir] [query]

OPTIONS

--refresh
キャッシュ(TTL 300 秒)を破棄して WORK_DIR を再走査する
work_dir
走査するディレクトリを一時的に切り替える
query
候補を部分一致で絞り込む。1 件なら fzf をスキップ

EXAMPLES

$ repo

fzf でリポジトリを選んで cd

$ repo dot

"dot" に一致する候補へ絞り込み。1 件なら即 cd

$ repo --refresh

キャッシュを無視して再走査

DEMO

DEMO GIF SLOT

src/assets/gifs/repo.gif を追加するとここに表示されます

SOURCE

# 作業ディレクトリごとに衝突しないキャッシュファイル名を返す内部関数
_repo_cache_file() {
    local work_dir="${1:-$WORK_DIR}"
    local cache_key
    cache_key=$(printf '%s' "$work_dir" | cksum | awk '{print $1}')
    printf '%s/repos-%s.tsv\n' "$REPO_CACHE_DIR" "$cache_key"
}

# キャッシュが TTL 内なら成功を返す内部関数
_repo_cache_is_fresh() {
    local cache_file="$1"
    [[ -f "$cache_file" ]] || return 1

    local modified_at
    modified_at=$(stat -f '%m' "$cache_file" 2>/dev/null) || return 1
    (( $(date +%s) - modified_at < REPO_CACHE_TTL ))
}

# Git リポジトリ候補をキャッシュ経由で出力する内部関数
# 第2引数が refresh の場合はキャッシュを使わず再走査する
_repo_choices() {
    local work_dir="${1:-$WORK_DIR}"
    local refresh="${2:-}"
    local cache_file
    cache_file=$(_repo_cache_file "$work_dir") || return 1

    if [[ "$refresh" != "refresh" ]] && _repo_cache_is_fresh "$cache_file"; then
        command cat "$cache_file"
        return
    fi

    mkdir -p "$REPO_CACHE_DIR" || return 1
    local cache_tmp="${cache_file}.tmp.$$"
    if _scan_repo_choices "$work_dir" > "$cache_tmp"; then
        mv "$cache_tmp" "$cache_file" || return 1
        command cat "$cache_file"
    else
        rm -f "$cache_tmp"
        return 1
    fi
}

# fzf でリポジトリを選んで cd する
# usage: repo [--refresh] [work_dir] [query]
# --refresh は work_dir の直後でも指定できる
# example: repo dot
repo() {
    local work_dir="$WORK_DIR"
    local refresh=""

    if [[ "${1:-}" == "--refresh" ]]; then
        refresh="refresh"
        shift
    fi

    # 既存の repo /path/to/work-dir 形式も維持する
    if [[ -n "${1:-}" && -d "$1" ]]; then
        work_dir="$1"
        shift
    fi

    if [[ "${1:-}" == "--refresh" ]]; then
        refresh="refresh"
        shift
    fi

    local query="$*"
    local selected_path
    selected_path=$(_select_repo_or_worktree "$work_dir" "$query" "$refresh") || return 1

    if [[ -n "$selected_path" ]]; then
        cd "$selected_path"
        echo "$selected_path" >> "$REPOHIST_FILE"
    fi
}
zsh/repo.zsh