├── .github └── workflows │ └── release.yml ├── .pre-commit-config.yaml ├── LICENSE ├── README.md ├── gh-f └── gh-f-logo.png /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 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 | [![asciicast](https://asciinema.org/a/EmYMmhOAn0dWAyBYrNqKk5AlR.svg)](https://asciinema.org/a/EmYMmhOAn0dWAyBYrNqKk5AlR) 46 | 47 | or a quick replay 48 | 49 | ![quick-demo](https://user-images.githubusercontent.com/15387611/157561174-d8bcfc81-0d12-4263-aeb9-19ba410ef40a.gif) 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 | 66 | | -s, search | search issues in any repository | interactive prompt: follow instructions | 67 | | -m, myissue | search issues you opened somewhere | interactive prompt: follow instructions | 68 | | -k, pick | cherrypick files between branches | enter: checkout cherrypicked files from branch | 69 | | -e, envs | show git config list | enter: print selected config variable | 70 | | -h, help | show help page | 71 | | -V, version | print the current version | 72 | 73 | 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. 74 | 75 | ## Customisation 76 | 77 | If you want to skip typing `gh f` before each command you may alias it directly, for instance 78 | 79 | ``` 80 | gh alias set prs 'f -p' # show PRs 81 | gh alias set l 'f -l' # show git logs 82 | ``` 83 | 84 | and likewise for the rest. 85 | 86 | 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. 87 | 88 | ## Feedback 89 | 90 | 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! 91 | 92 | See also the complete family of extensions 93 | 94 | - [gh-s](https://github.com/gennaro-tedesco/gh-s) to search for repositories with interactive prompt 95 | - [gh-i](https://github.com/gennaro-tedesco/gh-i) to search for github issues with interactive prompt 96 | -------------------------------------------------------------------------------- /gh-f: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | VERSION="gh-f 1.2.1" 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 | -s|search: search issues in any repository 27 | -m|myissue: search issues you opened somewhere 28 | -k|pick: cherrypick files from one branch to the other 29 | -e|envs: show git config list 30 | -h|help: show this help message 31 | -V|version: show the current version 32 | 33 | USAGE 34 | gh f -a | gh f adds 35 | 36 | multi-file selection with tab 37 | enter: add selected files to staging area 38 | ctrl-d: diff selected files 39 | 40 | gh f -r | gh f runs 41 | 42 | enter: search the selected run logs 43 | exits if no runs exist 44 | 45 | gh f -g | gh f greps 46 | 47 | prompt for pattern or pass regex as positional argument 48 | select files whose revisions contain pattern 49 | show pattern in revision history 50 | enter: show file at point in time of revision 51 | 52 | gh f -p | gh f prs 53 | 54 | manage open PRs 55 | 56 | enter: checkout the PR 57 | ctrl-d: diff the PR 58 | ctrl-v: view the PR 59 | 60 | gh f -b | gh f branches 61 | 62 | enter: checkout the branch (track if remote) 63 | ctrl-d: diff the branch 64 | ctrl-h: triple dot diff: ...HEAD 65 | ctrl-x: delete the branch (prompt for confirmation) 66 | 67 | gh f -d | gh f diffs 68 | 69 | prompt file extension to select 70 | enter: add all files with selected extension 71 | ctrl-d: diff all files with selected extension 72 | ctrl-x: checkout all files with selected extension 73 | 74 | gh f -l | gh f logs 75 | 76 | file paths can be passed as positional arguments 77 | enter: checkout selected commit 78 | ctrl-d: diff selected commit against current branch 79 | ctrl-v: view commit patch 80 | 81 | gh f -t | gh f tags 82 | 83 | enter: checkout the tag in detached HEAD state 84 | ctrl-d: diff the tag against current branch 85 | ctrl-v: show commit the tag points to 86 | 87 | gh f -s | gh f search 88 | 89 | prompt for repository name and issue title 90 | enter: view selected issue 91 | 92 | gh f -m | gh f myissue 93 | 94 | prompt for repository name 95 | enter: view selected issue 96 | 97 | gh f -k | gh f pick 98 | 99 | prompt for branch and file to cherrypick 100 | only show files that are present on both branches 101 | multi-file selection with tab 102 | enter: confirm branch and pick files 103 | 104 | gh f -e | gh f envs 105 | 106 | show git config --list of environment variables 107 | for the current git project 108 | 109 | CUSTOMISATION 110 | 111 | The prompt colours are defined via their ANSI sequences: if you like different ones you 112 | may change them yourself in the source code. 113 | 114 | The pager that is used for git diff and logs is defined in your own gitconfig, therefore 115 | independent of this extension. For runs and help page we default to bat or to less -R if 116 | the former is not detected in your system. 117 | 118 | " 119 | echo "$help_message" | $HELP_PAGER 120 | } 121 | 122 | is_remote() { 123 | [ ! "$(git ls-remote --heads origin 2>/dev/null | wc -l | tr -s ' ')" -eq 0 ] 124 | } 125 | 126 | is_git_repo() { 127 | [ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = "true" ] 128 | } 129 | 130 | adds() { 131 | if ! is_git_repo; then { 132 | echo "not a git repo" 133 | exit 134 | }; fi 135 | unstaged_list="$(git ls-files . --exclude-standard --others -m)" 136 | 137 | if [[ -n "$unstaged_list" ]]; then 138 | lines="$( 139 | echo "$unstaged_list" | 140 | fzf -d' ' \ 141 | --exit-0 \ 142 | --ansi --delimiter=: \ 143 | --multi \ 144 | --prompt="add files:" \ 145 | --preview="GH_FORCE_TTY=$FZF_PREVIEW_COLUMNS git diff --stat --color=always {1}" \ 146 | --expect="enter,ctrl-d" \ 147 | --header=$'Enter: add file(s), Ctrl-d: diff file(s)\n\n' \ 148 | --no-info 149 | 150 | )" 151 | 152 | key="$(head -1 <<<"$lines")" 153 | files="$(sed 1d <<<"$lines" | cut -d: -f2 | tr -d ' ')" 154 | 155 | case "$key" in 156 | enter) echo "$files" | xargs -n1 -t git add ;; 157 | ctrl-d) echo "$files" | xargs -n1 git diff ;; 158 | esac 159 | 160 | else 161 | echo "no files to stage" 162 | exit 163 | fi 164 | } 165 | 166 | runs() { 167 | if ! is_remote; then { 168 | echo "no gh remote found" 169 | exit 170 | }; fi 171 | lines="$( 172 | 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" \ 173 | '{print ID_COLOUR $7 SHELL_COLOUR": " TEXT_COLOUR $4 SHELL_COLOUR" - " STATUS_COLOUR $2 SHELL_COLOUR" - "$9}' | 174 | fzf -d' ' \ 175 | --exit-0 \ 176 | --ansi --delimiter=: \ 177 | --prompt="runs logs:" \ 178 | --preview='GH_FORCE_TTY=$FZF_PREVIEW_COLUMNS gh run view {1}' \ 179 | --expect="enter,ctrl-v,ctrl-o" \ 180 | --header=$'Enter: view run, Ctrl-v: view logs, Ctrl-o: view on web\n\n' \ 181 | --no-info 182 | )" 183 | 184 | key="$(head -1 <<<"$lines")" 185 | run_id="$(sed 1d <<<"$lines" | cut -d: -f1)" 186 | 187 | case "$key" in 188 | enter) gh run view -v "$run_id" ;; 189 | ctrl-v) gh run view --log "$run_id" | $LOG_PAGER ;; 190 | ctrl-o) gh run view -w "$run_id" ;; 191 | esac 192 | } 193 | 194 | greps() { 195 | if ! is_git_repo; then { 196 | echo "not a git repo" 197 | exit 198 | }; fi 199 | if [[ $# -eq 0 ]]; then 200 | read -rp "search regex: " regex 201 | else 202 | regex="$*" 203 | fi 204 | [[ -z "$regex" ]] && exit 205 | 206 | revision_files="$(git rev-list --all | xargs git grep "$regex" | awk -F':' '{print $2}' | sort -u)" 207 | [[ -z "$revision_files" ]] && { 208 | echo "no files containing $regex" 209 | return 210 | } 211 | selected_file="$( 212 | echo "$revision_files" | 213 | fzf -d' ' \ 214 | +s \ 215 | --ansi \ 216 | --prompt="files containing '$regex' in revisions:" 217 | )" 218 | 219 | revision="$( 220 | git rev-list --all --date-order -- $selected_file | xargs -I {} git grep "$regex" {} -- $selected_file | 221 | awk -F':' -v ID_COLOUR="$ID_COLOUR" '{print ID_COLOUR $1}' | uniq | 222 | fzf -d' ' \ 223 | +s \ 224 | --exit-0 \ 225 | --ansi \ 226 | --prompt="commits containing $regex in $selected_file:" \ 227 | --preview="git show {}:$selected_file | grep -n -C1 --color=always \"$regex\"" 228 | )" 229 | git show "$revision":$selected_file 230 | printf '\ncommit: %s\nfile: %s\n' "$revision" "$selected_file" 231 | } 232 | 233 | prs() { 234 | if ! is_remote; then { 235 | echo "no gh remote found" 236 | exit 237 | }; fi 238 | open_prs="$(gh pr list -s"open")" 239 | 240 | if [[ -n "$open_prs" ]]; then 241 | 242 | lines="$( 243 | echo "$open_prs" | awk -F'\t' -v ID_COLOUR="$ID_COLOUR" -v TEXT_COLOUR="$TEXT_COLOUR" -v SHELL_COLOUR="$SHELL_COLOUR" \ 244 | '{print ID_COLOUR $1 SHELL_COLOUR": " TEXT_COLOUR $3}' | 245 | fzf -d' ' \ 246 | --exit-0 \ 247 | --ansi --delimiter=: \ 248 | --prompt="open PRs:" \ 249 | --preview='GH_FORCE_TTY=$FZF_PREVIEW_COLUMNS gh pr view {1}' \ 250 | --expect="enter,ctrl-d,ctrl-v" \ 251 | --header=$'Enter: checkout PR, Ctrl-d: diff PR, Ctrl-v: view PR\n\n' \ 252 | --no-info 253 | 254 | )" 255 | 256 | key="$(head -1 <<<"$lines")" 257 | id="$(sed 1d <<<"$lines" | cut -d: -f1)" 258 | 259 | case "$key" in 260 | enter) gh pr checkout "$id" ;; 261 | ctrl-d) gh pr diff "$id" ;; 262 | ctrl-v) gh pr view "$id" ;; 263 | esac 264 | 265 | else 266 | echo "no open PRs" 267 | exit 268 | fi 269 | } 270 | 271 | branches() { 272 | if ! is_git_repo; then { 273 | echo "not a git repo" 274 | exit 275 | }; fi 276 | lines="$( 277 | git branch -a --sort=-committerdate | sed 's/[* ]//g' | grep -v 'HEAD' | 278 | fzf -d' ' \ 279 | --prompt="branches:" \ 280 | --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 {}" \ 281 | --expect "enter,ctrl-d,ctrl-h,ctrl-x" \ 282 | --header=$'Enter: checkout, Ctrl-d: diff branch, Ctrl-h: triple dot diff, Ctrl-x: delete branch\n\n' \ 283 | --no-info 284 | )" 285 | 286 | key="$(head -1 <<<"$lines")" 287 | branch="$(sed '1d;s/remotes\///g' <<<"$lines")" 288 | 289 | case "$key" in 290 | enter) if [[ $branch == *"origin"* ]]; then git checkout --track "$branch"; else git checkout "$branch"; fi ;; 291 | ctrl-d) echo "$branch <-> $(git branch --show-current)" && git diff "$branch" ;; 292 | ctrl-h) echo "$branch <-> $(git branch --show-current)" && git diff "$branch"...HEAD ;; 293 | ctrl-x) 294 | read -rp "Delete branch \"$branch\"? [y|n] " 295 | if [[ $REPLY =~ ^[Yy]$ ]]; then git branch -D "$branch"; fi 296 | ;; 297 | esac 298 | 299 | } 300 | 301 | diffs() { 302 | if ! is_git_repo; then { 303 | echo "not a git repo" 304 | exit 305 | }; fi 306 | lines="$( 307 | git diff --name-only | awk -F'.' '{print $NF}' | sort -u | 308 | fzf -d' ' \ 309 | --no-multi \ 310 | --exit-0 \ 311 | --prompt="select extension:" \ 312 | --preview="GH_FORCE_TTY=$FZF_PREVIEW_COLUMNS git diff --stat --color=always -- '*.{1}'" \ 313 | --expect="enter,ctrl-d,ctrl-x" \ 314 | --header=$'Enter: add file(s), Ctrl-d: diff file(s), Ctrl-x: checkout file(s)\n\n' \ 315 | --no-info 316 | )" 317 | 318 | key="$(head -1 <<<"$lines")" 319 | extension="$(sed 1d <<<"$lines" | cut -d: -f2 | tr -d ' ')" 320 | 321 | case "$key" in 322 | enter) git diff --name-only | grep ".*\.$extension" | xargs -n 1 -t git add ;; 323 | ctrl-d) git diff -- "*.$extension" ;; 324 | ctrl-x) git diff --name-only | grep ".*\.$extension" | xargs -n 1 -t git checkout ;; 325 | esac 326 | } 327 | 328 | logs() { 329 | if ! is_git_repo; then { 330 | echo "not a git repo" 331 | exit 332 | }; fi 333 | lines="$( 334 | git log --oneline --color=always "$@" | 335 | fzf -d' ' \ 336 | +s \ 337 | --ansi \ 338 | --prompt="$(git branch --show-current):" \ 339 | --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" \ 340 | --expect="enter,ctrl-d,ctrl-v" \ 341 | --header=$'Enter: checkout, Ctrl-d: diff, Ctrl-v: view commit patch\n\n' \ 342 | --no-info 343 | 344 | )" 345 | 346 | key="$(head -1 <<<"$lines")" 347 | commit="$(sed 1d <<<"$lines" | cut -d ' ' -f1 | tr -d ' ')" 348 | 349 | case "$key" in 350 | enter) git checkout "$commit" ;; 351 | ctrl-d) git diff "$commit" ;; 352 | ctrl-v) git show "$commit" ;; 353 | esac 354 | } 355 | 356 | tags() { 357 | if ! is_git_repo; then { 358 | echo "not a git repo" 359 | exit 360 | }; fi 361 | lines="$( 362 | 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' | 363 | fzf -d' ' \ 364 | --exit-0 \ 365 | --ansi --delimiter=: \ 366 | --prompt="tags:" \ 367 | --preview="GH_FORCE_TTY=$FZF_PREVIEW_COLUMNS git diff --stat --color=always {1} 2>/dev/null" \ 368 | --expect="enter,ctrl-d,ctrl-v" \ 369 | --header=$'Enter: checkout tag, Ctrl-d: diff tag, Ctrl-v: view tag commit\n\n' \ 370 | --no-info \ 371 | --no-sort 372 | )" 373 | 374 | key="$(head -1 <<<"$lines")" 375 | tag="$(sed 1d <<<"$lines" | cut -d ':' -f1 | tr -d ' ')" 376 | 377 | case "$key" in 378 | enter) git checkout tags/"$tag" ;; 379 | ctrl-d) git diff "$tag" ;; 380 | ctrl-v) git log -1 tags/"$tag" ;; 381 | esac 382 | } 383 | 384 | search() { 385 | read -rp "repository name: " repo 386 | [[ -z $repo ]] && exit 387 | read -rp "search: " search 388 | [[ -z $search ]] && exit 389 | issue_list="$(gh issue list -s"all" -R "$repo" -L 300 -S"$search")" 390 | 391 | if [[ -n $issue_list ]]; then 392 | 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" \ 393 | '{print ID_COLOUR $1 SHELL_COLOUR": " TEXT_COLOUR $3 SHELL_COLOUR" - " STATUS_COLOUR $2}' | 394 | fzf -d' ' \ 395 | --ansi \ 396 | --prompt="$search in $repo:")" 397 | else 398 | echo "$search not found in $repo" 399 | exit 400 | fi 401 | 402 | [[ -n $issue ]] && cut -f1 -d: <<<"$issue" | xargs -n1 gh issue view -R "$repo" -c 403 | } 404 | 405 | myissue() { 406 | read -rp "repository name: " repo 407 | [[ -z $repo ]] && exit 408 | issue_list="$(gh issue list -s"all" -R "$repo" -A @me)" 409 | 410 | if [[ -n $issue_list ]]; then 411 | 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" \ 412 | '{print ID_COLOUR $1 SHELL_COLOUR": " TEXT_COLOUR $3 SHELL_COLOUR" - " STATUS_COLOUR $2}' | 413 | fzf -d' ' \ 414 | --ansi \ 415 | --prompt="my issues in $repo:")" 416 | else 417 | echo "no issue found in $repo" 418 | exit 419 | fi 420 | 421 | [[ -n $issue ]] && cut -f1 -d: <<<"$issue" | xargs -n1 gh issue view -R "$repo" -c 422 | } 423 | 424 | pick() { 425 | if ! is_git_repo; then { 426 | echo "not a git repo" 427 | exit 428 | }; fi 429 | if [ "$(git branch | wc -l | tr -d ' ')" -eq 1 ]; then { 430 | echo "no alternate branch to pick from" 431 | exit 432 | }; fi 433 | from_branch=$(git branch | fzf -d' ' --prompt="pick from branch:" --no-info | tr -d ' ') 434 | [[ -n "$from_branch" ]] && files="$(git diff --name-only --diff-filter=M "$from_branch" | 435 | fzf -d' ' \ 436 | --multi \ 437 | --select-1 \ 438 | --exit-0 \ 439 | --preview="GH_FORCE_TTY=$FZF_PREVIEW_COLUMNS git diff --stat --color=always $from_branch {1} 2>/dev/null" \ 440 | --prompt="diff files:")" || echo "no branch selected" 441 | 442 | [[ -n ${files} ]] && git restore --source "${from_branch}" ${files} 443 | } 444 | 445 | envs() { 446 | config_var=$(git config --list | cut -d= -f1 | fzf -d' ' --prompt 'config list:' --no-info --preview='git config {}') 447 | [[ -n "$config_var" ]] && git config "$config_var" 448 | } 449 | 450 | # print help page 451 | [[ "$#" -eq 0 ]] && help 452 | 453 | while [[ "$#" -gt 0 ]]; do 454 | case $1 in 455 | -h | --help | help) 456 | help 457 | shift 458 | ;; 459 | -a | adds) 460 | adds 461 | shift 462 | ;; 463 | -r | runs) 464 | runs 465 | shift 466 | ;; 467 | -g | greps) 468 | shift 469 | greps "$@" 470 | shift 471 | ;; 472 | -p | prs) 473 | prs 474 | shift 475 | ;; 476 | -b | branches) 477 | branches 478 | shift 479 | ;; 480 | -d | diffs) 481 | diffs 482 | shift 483 | ;; 484 | -l | logs) 485 | shift 486 | logs "$@" 487 | shift 488 | ;; 489 | -t | tags) 490 | tags 491 | shift 492 | ;; 493 | -s | search) 494 | search 495 | shift 496 | ;; 497 | -m | myissue) 498 | myissue 499 | shift 500 | ;; 501 | -k | pick) 502 | pick 503 | shift 504 | ;; 505 | -e | envs) 506 | envs 507 | shift 508 | ;; 509 | -V | version) 510 | echo "$VERSION" 511 | shift 512 | ;; 513 | *) help ;; 514 | esac 515 | shift 516 | done 517 | -------------------------------------------------------------------------------- /gh-f-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gennaro-tedesco/gh-f/62e30628e53090ec761c239e40ec4965d5cd8b26/gh-f-logo.png --------------------------------------------------------------------------------