├── gh-f-logo.png ├── .pre-commit-config.yaml ├── .github └── workflows │ └── release.yml ├── LICENSE ├── README.md └── gh-f /gh-f-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gennaro-tedesco/gh-f/HEAD/gh-f-logo.png -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/jumanjihouse/pre-commit-hooks 3 | rev: 2.1.5 4 | hooks: 5 | - id: shfmt 6 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | tags: 5 | - "v*.*.*" 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | with: 14 | fetch-depth: 0 15 | - name: Release 16 | uses: softprops/action-gh-release@v2 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | 4 |
5 |

6 | 7 |

8 | 9 | releases 10 | 11 |

12 | 13 |

the ultimate compact fzf gh extension

14 |

15 | Installation • 16 | Usage • 17 | Customisation • 18 | Feedback 19 |

20 | 21 | `gh-f` is the ultimate, compact and snappy `fzf` extension for `gh` cli. It addresses most of your daily GitHub workflow, if not all of it: `f` stands for filter, fzf or fomething felse. 22 | 23 | ## Installation 24 | 25 | ``` 26 | gh extension install gennaro-tedesco/gh-f 27 | ``` 28 | 29 | ### Requirements 30 | 31 | - [fzf](https://github.com/junegunn/fzf) 32 | - [gnu core utils](https://www.gnu.org/software/coreutils/) 33 | - [bat](https://github.com/sharkdp/bat) (optional, if not detected it uses `less` as a pager, instead) 34 | 35 | ## Usage 36 | 37 | Get started! 38 | 39 | ``` 40 | gh f 41 | ``` 42 | 43 | shows the help page. Or watch it in action: 44 | 45 | ![Image](https://github.com/user-attachments/assets/9f844b23-ab3b-4a74-a26c-5c0a8c3dac61) 46 | 47 | ...and more 48 | 49 | ![Image](https://github.com/user-attachments/assets/cdbcd37e-3c10-4b26-ad6f-c122bb9a8f04) 50 | 51 | ``` 52 | gh f [cmd] 53 | ``` 54 | 55 | takes one of the following arguments or flags 56 | 57 | | command | description | binds | 58 | | :----------- | :---------------------------------------- | :------------------------------------------------------------------------------------------------ | 59 | | -a, adds | add files to staging area | enter: add files to staging area
ctrl-d: diff selected file | 60 | | -r, runs | show github workflow runs and filter logs | enter: search run logs | 61 | | -g, greps | grep in files in revision history | interactive prompt: insert regex, select files, show pattern in revision history | 62 | | -p, prs | view, diff and checkout PR | enter: checkout selected PR
ctrl-d: diff selected PR
ctrl-v: view selected PR | 63 | | -b, branches | checkout and diff branches | enter: checkout selected branch
ctrl-d: diff selected branch
ctrl-x: delete selected branch | 64 | | -l, logs | view, diff and checkout from logs history | enter: checkout selected commit
ctrl-d: diff selected commit
ctrl-v: view commit patch | 65 | | -t, tags | checkout and diff version tags | enter: checkout tag in detached HEAD
ctrl-d diff against current branch
ctrl-v: view tag commit | 66 | | -R, releases | checkout and diff release tags | enter: checkout tag in detached HEAD
ctrl-d diff against current branch
ctrl-v: view release commit | 67 | | -s, search | search issues in any repository | interactive prompt: follow instructions | 68 | | -m, myissue | search issues you opened somewhere | interactive prompt: follow instructions | 69 | | -k, pick | cherrypick files between branches | enter: checkout cherrypicked files from branch | 70 | | -e, envs | show git config list | enter: print selected config variable | 71 | | -h, help | show help page | 72 | | -V, version | print the current version | 73 | 74 | Most commands follow the semantics of `git` standard instructions (so that you can remember them easily), only we append an `s` to avoid conflicts, should you have those commands lying around in the shell as aliases or for scripting. 75 | 76 | ## Customisation 77 | 78 | If you want to skip typing `gh f` before each command you may alias it directly, for instance 79 | 80 | ``` 81 | gh alias set prs 'f -p' # show PRs 82 | gh alias set l 'f -l' # show git logs 83 | ``` 84 | 85 | and likewise for the rest. 86 | 87 | The prompt colours are defined via their ANSI sequences [here](https://github.com/gennaro-tedesco/gh-f/blob/37157bb30c2bceb99a5c9d6d199c543ce347690f/gh-f#L6-L9): change yourself accordingly, if you like different ones. Moreover notice that the rendering of text in the `fzf` preview window follows the options set in the `FZF_DEFAULT_OPTS` [env variable](https://github.com/junegunn/fzf#environment-variables): if you want to replicate exactly what is shown in the demo, [here](https://github.com/gennaro-tedesco/dotfiles/blob/132eeec7d9573ca3ccd47a078d19029b75010b2b/zsh/zshrc#L43) are my settings. 88 | 89 | ## Feedback 90 | 91 | If you find this application useful consider awarding it a ⭐, it is a great way to give feedback! Otherwise, any additional suggestions or merge request is warmly welcome! 92 | 93 | See also the complete family of extensions 94 | 95 | - [gh-s](https://github.com/gennaro-tedesco/gh-s) to search for repositories with interactive prompt 96 | - [gh-i](https://github.com/gennaro-tedesco/gh-i) to search for github issues with interactive prompt 97 | -------------------------------------------------------------------------------- /gh-f: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | VERSION="gh-f 1.8.0" 5 | 6 | ID_COLOUR="\033[36m" 7 | TEXT_COLOUR="\033[34m" 8 | STATUS_COLOUR="\033[33m" 9 | SHELL_COLOUR="\033[0m" 10 | [[ -z $(command -v bat) ]] && HELP_PAGER="less -R" || HELP_PAGER="bat -l man -p" 11 | [[ -z $(command -v bat) ]] && LOG_PAGER="less -R" || LOG_PAGER="bat -l log -p" 12 | 13 | help() { 14 | help_message=" 15 | gh f [cmd] 16 | 17 | COMMANDS 18 | -a|adds: add to staging area 19 | -r|runs: show github workflow runs and filter logs 20 | -g|greps: grep in files in revision history 21 | -p|prs: view, diff and checkout open PR 22 | -b|branches: checkout and diff branches 23 | -d|diffs: diff files by extension 24 | -l|logs: select commits and show diff 25 | -t|tags: checkout and diff version tags 26 | -R|releases: show and diff github releases 27 | -s|search: search issues in any repository 28 | -m|myissue: search issues you opened somewhere 29 | -k|pick: cherrypick files from one branch to the other 30 | -e|envs: show git config list 31 | -h|help: show this help message 32 | -V|version: show the current version 33 | 34 | USAGE 35 | gh f -a | gh f adds 36 | 37 | multi-file selection with tab 38 | enter: add selected files to staging area 39 | ctrl-d: diff selected files 40 | 41 | gh f -r | gh f runs 42 | 43 | enter: search the selected run logs 44 | exits if no runs exist 45 | 46 | gh f -g | gh f greps 47 | 48 | prompt for pattern or pass regex as positional argument 49 | select files whose revisions contain pattern 50 | show pattern in revision history 51 | enter: show file at point in time of revision 52 | 53 | gh f -p | gh f prs 54 | 55 | manage open PRs 56 | 57 | enter: checkout the PR 58 | ctrl-d: diff the PR 59 | ctrl-v: view the PR 60 | 61 | gh f -b | gh f branches 62 | 63 | enter: checkout the branch (track if remote) 64 | ctrl-d: diff the branch 65 | ctrl-s: triple dot diff: ...HEAD 66 | ctrl-x: delete the branch (prompt for confirmation) 67 | 68 | gh f -d | gh f diffs 69 | 70 | prompt file extension to select 71 | enter: add all files with selected extension 72 | ctrl-d: diff all files with selected extension 73 | ctrl-x: checkout all files with selected extension 74 | 75 | gh f -l | gh f logs 76 | 77 | file paths can be passed as positional arguments 78 | enter: checkout selected commit 79 | ctrl-d: diff selected commit against current branch 80 | ctrl-v: view commit patch 81 | 82 | gh f -t | gh f tags 83 | 84 | enter: checkout the tag in detached HEAD state 85 | ctrl-d: diff the tag against current branch 86 | ctrl-v: show commit the tag points to 87 | 88 | gh f -R | gh f releases 89 | 90 | enter: checkout the release tag in detached HEAD state 91 | ctrl-d: diff the release tag against current branch 92 | ctrl-v: show commit the release tag points to 93 | 94 | gh f -s | gh f search 95 | 96 | prompt for repository name and issue title 97 | enter: view selected issue 98 | 99 | gh f -m | gh f myissue 100 | 101 | prompt for repository name 102 | enter: view selected issue 103 | 104 | gh f -k | gh f pick 105 | 106 | prompt for branch and file to cherrypick 107 | only show files that are present on both branches 108 | multi-file selection with tab 109 | enter: confirm branch and pick files 110 | 111 | gh f -e | gh f envs 112 | 113 | show git config --list of environment variables 114 | for the current git project 115 | 116 | CUSTOMISATION 117 | 118 | The prompt colours are defined via their ANSI sequences: if you like different ones you 119 | may change them yourself in the source code. 120 | 121 | The pager that is used for git diff and logs is defined in your own gitconfig, therefore 122 | independent of this extension. For runs and help page we default to bat or to less -R if 123 | the former is not detected in your system. 124 | 125 | " 126 | echo "$help_message" | $HELP_PAGER 127 | } 128 | 129 | is_remote() { 130 | [ ! "$(git ls-remote --heads origin 2>/dev/null | wc -l | tr -s ' ')" -eq 0 ] 131 | } 132 | 133 | is_git_repo() { 134 | [ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = "true" ] 135 | } 136 | 137 | adds() { 138 | if ! is_git_repo; then { 139 | echo "not a git repo" 140 | exit 141 | }; fi 142 | unstaged_list="$(git ls-files . --exclude-standard --others -m)" 143 | 144 | if [[ -n "$unstaged_list" ]]; then 145 | lines="$( 146 | echo "$unstaged_list" | 147 | fzf -d' ' \ 148 | --exit-0 \ 149 | --ansi --delimiter=: \ 150 | --multi \ 151 | --ghost="add files:" \ 152 | --bind="focus:transform-footer:GH_FORCE_TTY=$FZF_PREVIEW_COLUMNS git diff --stat --color=always -- {1} __NOVAR__" \ 153 | --expect="enter,ctrl-d" \ 154 | --header=$'Enter: add file(s), Ctrl-d: diff file(s)\n\n' \ 155 | --no-info 156 | 157 | )" 158 | 159 | key="$(head -1 <<<"$lines")" 160 | files="$(sed 1d <<<"$lines" | cut -d: -f2 | tr -d ' ')" 161 | 162 | case "$key" in 163 | enter) echo "$files" | xargs -n1 -t git add ;; 164 | ctrl-d) echo "$files" | xargs -n1 git diff ;; 165 | esac 166 | 167 | else 168 | echo "no files to stage" 169 | exit 170 | fi 171 | } 172 | 173 | runs() { 174 | if ! is_remote; then { 175 | echo "no gh remote found" 176 | exit 177 | }; fi 178 | lines="$( 179 | gh run list -L100 | awk -F'\t' -v ID_COLOUR="$ID_COLOUR" -v TEXT_COLOUR="$TEXT_COLOUR" -v STATUS_COLOUR="$STATUS_COLOUR" -v SHELL_COLOUR="$SHELL_COLOUR" \ 180 | '{print ID_COLOUR $7 SHELL_COLOUR": " TEXT_COLOUR $4 STATUS_COLOUR" ("$2")" SHELL_COLOUR": " $5}' | 181 | fzf -d' ' \ 182 | --exit-0 \ 183 | --ansi --delimiter=: \ 184 | --ghost="select run" \ 185 | --preview='GH_FORCE_TTY=$FZF_PREVIEW_COLUMNS gh run view -v {1}' \ 186 | --expect="enter,ctrl-v,ctrl-o" \ 187 | --header=$'Enter: view run, Ctrl-v: view logs, Ctrl-o: view on web\n\n' \ 188 | --no-info 189 | )" 190 | 191 | key="$(head -1 <<<"$lines")" 192 | run_id="$(sed 1d <<<"$lines" | cut -d: -f1)" 193 | 194 | case "$key" in 195 | enter) gh run view -v "$run_id" ;; 196 | ctrl-v) gh run view --log "$run_id" | $LOG_PAGER ;; 197 | ctrl-o) gh run view -w "$run_id" ;; 198 | esac 199 | } 200 | 201 | greps() { 202 | if ! is_git_repo; then { 203 | echo "not a git repo" 204 | exit 205 | }; fi 206 | if [[ $# -eq 0 ]]; then 207 | read -rp "search regex: " regex 208 | else 209 | regex="$*" 210 | fi 211 | [[ -z "$regex" ]] && exit 212 | 213 | revision_files="$(git rev-list --all | xargs git grep "$regex" | awk -F':' '{print $2}' | sort -u)" 214 | [[ -z "$revision_files" ]] && { 215 | echo "no files containing $regex" 216 | return 217 | } 218 | selected_file="$( 219 | echo "$revision_files" | 220 | fzf -d' ' \ 221 | +s \ 222 | --ansi \ 223 | --ghost='file name' \ 224 | --footer="search regex: $regex" 225 | )" 226 | 227 | revision="$( 228 | git rev-list --all --date-order -- $selected_file | xargs -I {} git grep "$regex" {} -- $selected_file | 229 | awk -F':' -v ID_COLOUR="$ID_COLOUR" '{print ID_COLOUR $1}' | uniq | 230 | fzf -d' ' \ 231 | +s \ 232 | --exit-0 \ 233 | --ansi \ 234 | --ghost="select commit" \ 235 | --footer="search regex: $regex in $selected_file" \ 236 | --preview="git show {}:$selected_file | grep -n -C1 --color=always \"$regex\"" 237 | )" 238 | git show "$revision":$selected_file 239 | printf '\ncommit: %s\nfile: %s\n' "$revision" "$selected_file" 240 | } 241 | 242 | prs() { 243 | if ! is_remote; then { 244 | echo "no gh remote found" 245 | exit 246 | }; fi 247 | open_prs="$(gh pr list "$@" -s"open")" 248 | 249 | if [[ -n "$open_prs" ]]; then 250 | 251 | lines="$( 252 | echo "$open_prs" | awk -F'\t' -v ID_COLOUR="$ID_COLOUR" -v TEXT_COLOUR="$TEXT_COLOUR" -v SHELL_COLOUR="$SHELL_COLOUR" \ 253 | '{print ID_COLOUR $1 SHELL_COLOUR": " TEXT_COLOUR $2}' | 254 | fzf -d' ' \ 255 | --exit-0 \ 256 | --ansi --delimiter=: \ 257 | --ghost="select PR" \ 258 | --preview='GH_FORCE_TTY=$FZF_PREVIEW_COLUMNS gh pr view {1}' \ 259 | --expect="enter,ctrl-d,ctrl-v" \ 260 | --header=$'Enter: checkout PR, Ctrl-d: diff PR, Ctrl-v: view PR\n\n' \ 261 | --no-info 262 | 263 | )" 264 | 265 | key="$(head -1 <<<"$lines")" 266 | id="$(sed 1d <<<"$lines" | cut -d: -f1)" 267 | 268 | case "$key" in 269 | enter) gh pr checkout "$id" ;; 270 | ctrl-d) gh pr diff "$id" ;; 271 | ctrl-v) gh pr view "$id" ;; 272 | esac 273 | 274 | else 275 | echo "no open PRs" 276 | exit 277 | fi 278 | } 279 | 280 | branches() { 281 | if ! is_git_repo; then { 282 | echo "not a git repo" 283 | exit 284 | }; fi 285 | lines="$( 286 | git branch -a --sort=-committerdate | sed 's/[* ]//g' | grep -v 'HEAD' | 287 | fzf -d' ' \ 288 | --ghost="select branch" \ 289 | --preview="git log --oneline --format='%C(bold blue)%h%C(reset) - %C(green)%ar%C(reset) - %C(cyan)%an%C(reset)%C(bold yellow)%d%C(reset): %s' --color=always {}" \ 290 | --expect "enter,ctrl-d,ctrl-s,ctrl-x" \ 291 | --header=$'Enter: checkout, Ctrl-d: diff branch, Ctrl-s: triple dot diff, Ctrl-x: delete branch\n\n' \ 292 | --no-info 293 | )" 294 | 295 | key="$(head -1 <<<"$lines")" 296 | branch="$(sed '1d;s/remotes\///g' <<<"$lines")" 297 | 298 | case "$key" in 299 | enter) if [[ $branch == *"origin"* ]]; then git checkout --track "$branch"; else git checkout "$branch"; fi ;; 300 | ctrl-d) echo "$branch <-> $(git branch --show-current)" && git diff "$branch" ;; 301 | ctrl-s) echo "$branch <-> $(git branch --show-current)" && git diff "$branch"...HEAD ;; 302 | ctrl-x) 303 | read -rp "Delete branch \"$branch\"? [y|n] " 304 | if [[ $REPLY =~ ^[Yy]$ ]]; then git branch -D "$branch"; fi 305 | ;; 306 | esac 307 | 308 | } 309 | 310 | diffs() { 311 | if ! is_git_repo; then { 312 | echo "not a git repo" 313 | exit 314 | }; fi 315 | lines="$( 316 | git diff --name-only | awk -F'.' '{print $NF}' | sort -u | 317 | fzf -d' ' \ 318 | --no-multi \ 319 | --exit-0 \ 320 | --ghost="select extension:" \ 321 | --preview="GH_FORCE_TTY=$FZF_PREVIEW_COLUMNS git diff --stat --color=always -- '*.{1}'" \ 322 | --expect="enter,ctrl-d,ctrl-x" \ 323 | --header=$'Enter: add file(s), Ctrl-d: diff file(s), Ctrl-x: checkout file(s)\n\n' \ 324 | --no-info 325 | )" 326 | 327 | key="$(head -1 <<<"$lines")" 328 | extension="$(sed 1d <<<"$lines" | cut -d: -f2 | tr -d ' ')" 329 | 330 | case "$key" in 331 | enter) git diff --name-only | grep ".*\.$extension" | xargs -n 1 -t git add ;; 332 | ctrl-d) git diff -- "*.$extension" ;; 333 | ctrl-x) git diff --name-only | grep ".*\.$extension" | xargs -n 1 -t git checkout ;; 334 | esac 335 | } 336 | 337 | logs() { 338 | if ! is_git_repo; then { 339 | echo "not a git repo" 340 | exit 341 | }; fi 342 | lines="$( 343 | git log --oneline --color=always "$@" | 344 | fzf -d' ' \ 345 | +s \ 346 | --ansi \ 347 | --ghost="select commit" \ 348 | --preview="git show --stat --abbrev-commit {1} --format='%C(bold blue)%h%C(reset) - %C(green)%ar%C(reset): %C(cyan)%an%C(reset)%C(bold yellow)%d%C(reset)' --color=always" \ 349 | --expect="enter,ctrl-d,ctrl-v" \ 350 | --header=$'Enter: checkout, Ctrl-d: diff, Ctrl-v: view commit patch\n\n' \ 351 | --no-info 352 | 353 | )" 354 | 355 | key="$(head -1 <<<"$lines")" 356 | commit="$(sed 1d <<<"$lines" | cut -d ' ' -f1 | tr -d ' ')" 357 | 358 | case "$key" in 359 | enter) git checkout "$commit" ;; 360 | ctrl-d) git diff "$commit" ;; 361 | ctrl-v) git show "$commit" ;; 362 | esac 363 | } 364 | 365 | tags() { 366 | if ! is_git_repo; then { 367 | echo "not a git repo" 368 | exit 369 | }; fi 370 | lines="$( 371 | git tag -l -n --sort=-version:refname | awk -F' ' -v ID_COLOUR="$ID_COLOUR" -v TEXT_COLOUR="$TEXT_COLOUR" -v SHELL_COLOUR="$SHELL_COLOUR" '{first = $1; $1=""; print ID_COLOUR first SHELL_COLOUR" :" TEXT_COLOUR $0}' | sed 's/^ //g' | 372 | fzf -d' ' \ 373 | --exit-0 \ 374 | --ansi --delimiter=: \ 375 | --ghost="select tag" \ 376 | --preview="GH_FORCE_TTY=$FZF_PREVIEW_COLUMNS git diff --stat --color=always {1} 2>/dev/null" \ 377 | --expect="enter,ctrl-d,ctrl-v" \ 378 | --header=$'Enter: checkout tag, Ctrl-d: diff tag, Ctrl-v: view tag commit\n\n' \ 379 | --no-info \ 380 | --no-sort 381 | )" 382 | 383 | key="$(head -1 <<<"$lines")" 384 | tag="$(sed 1d <<<"$lines" | cut -d ':' -f1 | tr -d ' ')" 385 | 386 | case "$key" in 387 | enter) git checkout tags/"$tag" ;; 388 | ctrl-d) git diff "$tag" ;; 389 | ctrl-v) git log -1 tags/"$tag" ;; 390 | esac 391 | } 392 | 393 | releases() { 394 | if ! is_remote; then { 395 | echo "no gh remote found" 396 | exit 397 | }; fi 398 | 399 | lines="$( 400 | gh release list -L100 | awk -F'\t' -v TEXT_COLOUR="$TEXT_COLOUR" -v SHELL_COLOUR="$SHELL_COLOUR" '{print TEXT_COLOUR $3 SHELL_COLOUR": " substr($4,1,10)}' | 401 | fzf -d' ' \ 402 | --exit-0 \ 403 | --ansi --delimiter=: \ 404 | --ghost="select release" \ 405 | --preview="GH_FORCE_TTY=$FZF_PREVIEW_COLUMNS git diff --stat --color=always {1} 2>/dev/null" \ 406 | --expect="enter,ctrl-d,ctrl-v" \ 407 | --header=$'Enter: checkout tag, Ctrl-d: diff tag, Ctrl-v: view tag commit\n\n' \ 408 | --no-info \ 409 | --no-sort 410 | )" 411 | 412 | key="$(head -1 <<<"$lines")" 413 | tag="$(sed 1d <<<"$lines" | cut -d: -f1 | tr -d ' ')" 414 | 415 | case "$key" in 416 | enter) git checkout tags/"$tag" ;; 417 | ctrl-d) git diff "$tag" ;; 418 | ctrl-v) gh release view "$tag" ;; 419 | esac 420 | } 421 | 422 | search() { 423 | read -rp "repository name: " repo 424 | [[ -z $repo ]] && exit 425 | read -rp "search: " search 426 | [[ -z $search ]] && exit 427 | issue_list="$(gh issue list -s"all" -R "$repo" -L 300 -S"$search")" 428 | 429 | if [[ -n $issue_list ]]; then 430 | issue="$( 431 | echo "$issue_list" | awk -F'\t' -v ID_COLOUR="$ID_COLOUR" -v TEXT_COLOUR="$TEXT_COLOUR" -v STATUS_COLOUR="$STATUS_COLOUR" -v SHELL_COLOUR="$SHELL_COLOUR" \ 432 | '{print ID_COLOUR $1 SHELL_COLOUR": " TEXT_COLOUR $3 SHELL_COLOUR" - " STATUS_COLOUR $2}' | 433 | fzf -d' ' \ 434 | --ansi \ 435 | --ghost="select issue" 436 | )" 437 | else 438 | echo "$search not found in $repo" 439 | exit 440 | fi 441 | 442 | [[ -n $issue ]] && cut -f1 -d: <<<"$issue" | xargs -n1 gh issue view -R "$repo" -c 443 | } 444 | 445 | myissue() { 446 | read -rp "repository name: " repo 447 | [[ -z $repo ]] && exit 448 | issue_list="$(gh issue list -s"all" -R "$repo" -A @me)" 449 | 450 | if [[ -n $issue_list ]]; then 451 | issue="$(echo "$issue_list" | awk -F'\t' -v ID_COLOUR="$ID_COLOUR" -v TEXT_COLOUR="$TEXT_COLOUR" -v STATUS_COLOUR="$STATUS_COLOUR" -v SHELL_COLOUR="$SHELL_COLOUR" \ 452 | '{print ID_COLOUR $1 SHELL_COLOUR": " TEXT_COLOUR $3 SHELL_COLOUR" - " STATUS_COLOUR $2}' | 453 | fzf -d' ' \ 454 | --ansi \ 455 | --ghost="select issue")" 456 | else 457 | echo "no issue found in $repo" 458 | exit 459 | fi 460 | 461 | [[ -n $issue ]] && cut -f1 -d: <<<"$issue" | xargs -n1 gh issue view -R "$repo" -c 462 | } 463 | 464 | pick() { 465 | if ! is_git_repo; then { 466 | echo "not a git repo" 467 | exit 468 | }; fi 469 | if [ "$(git branch | wc -l | tr -d ' ')" -eq 1 ]; then { 470 | echo "no alternate branch to pick from" 471 | exit 472 | }; fi 473 | from_branch=$(git branch | fzf -d' ' --ghost="pick from branch" --no-info | tr -d ' ') 474 | [[ -n "$from_branch" ]] && files="$(git diff --name-only --diff-filter=M "$from_branch" | 475 | fzf -d' ' \ 476 | --multi \ 477 | --select-1 \ 478 | --exit-0 \ 479 | --preview="GH_FORCE_TTY=$FZF_PREVIEW_COLUMNS git diff --stat --color=always $from_branch {1} 2>/dev/null" \ 480 | --ghost="select files:")" || echo "no branch selected" 481 | 482 | [[ -n ${files} ]] && git restore --source "${from_branch}" ${files} 483 | } 484 | 485 | envs() { 486 | config_var=$(git config --list | cut -d= -f1 | fzf -d' ' --ghost 'select value' --no-info --bind 'focus:transform-footer:git config {}') 487 | [[ -n "$config_var" ]] && git config "$config_var" 488 | } 489 | 490 | # print help page 491 | [[ "$#" -eq 0 ]] && help 492 | 493 | while [[ "$#" -gt 0 ]]; do 494 | case $1 in 495 | -h | --help | help) 496 | help 497 | shift 498 | ;; 499 | -a | adds) 500 | adds 501 | shift 502 | ;; 503 | -r | runs) 504 | runs 505 | shift 506 | ;; 507 | -g | greps) 508 | shift 509 | greps "$@" 510 | shift 511 | ;; 512 | -p | prs) 513 | shift 514 | prs "$@" 515 | shift 516 | ;; 517 | -b | branches) 518 | branches 519 | shift 520 | ;; 521 | -d | diffs) 522 | diffs 523 | shift 524 | ;; 525 | -l | logs) 526 | shift 527 | logs "$@" 528 | shift 529 | ;; 530 | -t | tags) 531 | tags 532 | shift 533 | ;; 534 | -R | releases) 535 | releases 536 | shift 537 | ;; 538 | -s | search) 539 | search 540 | shift 541 | ;; 542 | -m | myissue) 543 | myissue 544 | shift 545 | ;; 546 | -k | pick) 547 | pick 548 | shift 549 | ;; 550 | -e | envs) 551 | envs 552 | shift 553 | ;; 554 | -V | version) 555 | echo "$VERSION" 556 | shift 557 | ;; 558 | *) help ;; 559 | esac 560 | shift 561 | done 562 | --------------------------------------------------------------------------------