From 9f480c470f1dd0cfaa0b4ef62f277f6e4aefc602 Mon Sep 17 00:00:00 2001 From: sgasse <47532708+sgasse@users.noreply.github.com> Date: Wed, 29 Mar 2023 13:42:19 +0200 Subject: [PATCH] custom-completions: git: Expand completions (#407) The different commands such as `git checkout`, `git switch`, `git cherry-pick` and `git rebase` all accept slightly differing refspecs. This commit separates the extraction of refspecs and combines them in individual completion commands for the different external commands. All git commands complete with a table of `value | description` now. Addresses #406 Co-authored-by: Simon Gasse --- custom-completions/git/git-completions.nu | 163 ++++++++++++++-------- 1 file changed, 102 insertions(+), 61 deletions(-) diff --git a/custom-completions/git/git-completions.nu b/custom-completions/git/git-completions.nu index 36b339c..e943ed2 100644 --- a/custom-completions/git/git-completions.nu +++ b/custom-completions/git/git-completions.nu @@ -1,16 +1,3 @@ -def "nu-complete git branches" [] { - ^git branch | lines | each { |line| $line | str replace '\* ' "" | str trim } -} - -def "nu-complete git switchable branches" [] { - let remotes_regex = (["(", ((nu-complete git remotes | each {|r| ['remotes/', $r, '/'] | str join}) | str join "|"), ")"] | str join) - ^git branch -a - | lines - | parse -r (['^[\* ]+', $remotes_regex, '?(?P\S+)'] | flatten | str join) - | get branch - | uniq - | where {|branch| $branch != "HEAD"} -} def "nu-complete git available upstream" [] { ^git branch -a | lines | each { |line| $line | str replace '\* ' "" | str trim } @@ -24,15 +11,69 @@ def "nu-complete git log" [] { ^git log --pretty=%h | lines | each { |line| $line | str trim } } -def "nu-complete git commits" [] { +# Yield all existing commits in descending chronological order. +def "nu-complete git commits all" [] { ^git rev-list --all --remotes --pretty=oneline | lines | parse "{value} {description}" } -def "nu-complete git branches and commits" [] { - nu-complete git switchable branches +# Yield commits of current branch only. This is useful for e.g. cut points in +# `git rebase`. +def "nu-complete git commits current branch" [] { + ^git log --pretty="%h %s" | lines | parse "{value} {description}" +} + +# Yield local branches like `main`, `feature/typo_fix` +def "nu-complete git local branches" [] { + ^git branch | lines | each { |line| $line | str replace '\* ' "" | str trim } +} + +# Yield remote branches like `origin/main`, `upstream/feature-a` +def "nu-complete git remote branches with prefix" [] { + ^git branch -r | lines | parse -r '^\*?(\s*|\s*\S* -> )(?P\S*$)' | get branch | uniq +} + +# Yield remote branches *without* prefix which do not have a local counterpart. +# E.g. `upstream/feature-a` as `feature-a` to checkout and track in one command +# with `git checkout` or `git switch`. +def "nu-complete git remote branches nonlocal without prefix" [] { + # Get regex to strip remotes prefixes. It will look like `(origin|upstream)` + # for the two remotes `origin` and `upstream`. + let remotes_regex = (["(", ((nu-complete git remotes | each {|r| [$r, '/'] | str join}) | str join "|"), ")"] | str join) + let local_branches = (nu-complete git local branches) + ^git branch -r | lines | parse -r (['^[\* ]+', $remotes_regex, '?(?P\S+)'] | flatten | str join) | get branch | uniq | where {|branch| $branch != "HEAD"} | where {|branch| $branch not-in $local_branches } +} + +def "nu-complete git switch" [] { + (nu-complete git local branches) | parse "{value}" - | insert description Branch - | append (nu-complete git commits) + | insert description "local branch" + | append (nu-complete git remote branches nonlocal without prefix + | parse "{value}" + | insert description "remote branch") +} + +def "nu-complete git checkout" [] { + (nu-complete git local branches) + | parse "{value}" + | insert description "local branch" + | append (nu-complete git remote branches nonlocal without prefix + | parse "{value}" + | insert description "remote branch") + | append (nu-complete git remote branches with prefix + | parse "{value}" + | insert description "remote branch") + | append (nu-complete git commits all) +} + +# Arguments to `git rebase --onto ` +def "nu-complete git rebase" [] { + (nu-complete git local branches) + | parse "{value}" + | insert description "local branch" + | append (nu-complete git remote branches with prefix + | parse "{value}" + | insert description "remote branch") + | append (nu-complete git commits all) } def "nu-complete git stash-list" [] { @@ -61,7 +102,7 @@ def "nu-complete git subcommands" [] { # Check out git branches and files export extern "git checkout" [ - ...targets: string@"nu-complete git switchable branches" # name of the branch or files to checkout + ...targets: string@"nu-complete git checkout" # name of the branch or files to checkout --conflict: string # conflict style (merge or diff3) --detach(-d) # detach HEAD at named commit --force(-f) # force checkout (throw away local modifications) @@ -135,45 +176,45 @@ export extern "git fetch" [ # Push changes export extern "git push" [ - remote?: string@"nu-complete git remotes", # the name of the remote - ...refs: string@"nu-complete git branches" # the branch / refspec - --all # push all refs - --atomic # request atomic transaction on remote side - --delete(-d) # delete refs - --dry-run(-n) # dry run - --exec: string # receive pack program - --follow-tags # push missing but relevant tags - --force-with-lease # require old value of ref to be at this value - --force(-f) # force updates - --ipv4(-4) # use IPv4 addresses only - --ipv6(-6) # use IPv6 addresses only - --mirror # mirror all refs - --no-verify # bypass pre-push hook - --porcelain # machine-readable output - --progress # force progress reporting - --prune # prune locally removed refs - --push-option(-o): string # option to transmit - --quiet(-q) # be more quiet - --receive-pack: string # receive pack program - --recurse-submodules: string # control recursive pushing of submodules - --repo: string # repository - --set-upstream(-u) # set upstream for git pull/status - --signed: string # GPG sign the push - --tags # push tags (can't be used with --all or --mirror) - --thin # use thin pack - --verbose(-v) # be more verbose + remote?: string@"nu-complete git remotes", # the name of the remote + ...refs: string@"nu-complete git local branches" # the branch / refspec + --all # push all refs + --atomic # request atomic transaction on remote side + --delete(-d) # delete refs + --dry-run(-n) # dry run + --exec: string # receive pack program + --follow-tags # push missing but relevant tags + --force-with-lease # require old value of ref to be at this value + --force(-f) # force updates + --ipv4(-4) # use IPv4 addresses only + --ipv6(-6) # use IPv6 addresses only + --mirror # mirror all refs + --no-verify # bypass pre-push hook + --porcelain # machine-readable output + --progress # force progress reporting + --prune # prune locally removed refs + --push-option(-o): string # option to transmit + --quiet(-q) # be more quiet + --receive-pack: string # receive pack program + --recurse-submodules: string # control recursive pushing of submodules + --repo: string # repository + --set-upstream(-u) # set upstream for git pull/status + --signed: string # GPG sign the push + --tags # push tags (can't be used with --all or --mirror) + --thin # use thin pack + --verbose(-v) # be more verbose ] # Pull changes export extern "git pull" [ - remote?: string@"nu-complete git remotes", # the name of the remote - ...refs: string@"nu-complete git branches" # the branch / refspec - --rebase # rebase current branch on top of upstream after fetching + remote?: string@"nu-complete git remotes", # the name of the remote + ...refs: string@"nu-complete git local branches" # the branch / refspec + --rebase # rebase current branch on top of upstream after fetching ] # Switch between branches and commits export extern "git switch" [ - switch?: string@"nu-complete git switchable branches" # name of branch to switch to + switch?: string@"nu-complete git switch" # name of branch to switch to --create(-c): string # create a new branch --detach(-d): string@"nu-complete git log" # switch to a commit in a detatched state --force-create(-C): string # forces creation of new branch, if it exists then the existing branch will be reset to starting point @@ -194,7 +235,7 @@ export extern "git switch" [ # Apply the change introduced by an existing commit export extern "git cherry-pick" [ - commit?: string@"nu-complete git commits" # The commit ID to be cherry-picked + commit?: string@"nu-complete git commits all" # The commit ID to be cherry-picked --edit(-e) # Edit the commit message prior to committing --no-commit(-n) # Apply changes without making any commit --signoff(-s) # Add Signed-off-by line to the commit message @@ -206,19 +247,19 @@ export extern "git cherry-pick" [ # Rebase the current branch export extern "git rebase" [ - branch?: string@"nu-complete git branches and commits" # name of the branch to rebase onto - upstream?: string@"nu-complete git branches and commits" # upstream branch to compare against - --continue # restart rebasing process after editing/resolving a conflict - --abort # abort rebase and reset HEAD to original branch - --quit # abort rebase but do not reset HEAD - --interactive(-i) # rebase interactively with list of commits in editor - --onto: string@"nu-complete git branches and commits" # starting point at which to create the new commits - --root # start rebase from root commit + branch?: string@"nu-complete git rebase" # name of the branch to rebase onto + upstream?: string@"nu-complete git rebase" # upstream branch to compare against + --continue # restart rebasing process after editing/resolving a conflict + --abort # abort rebase and reset HEAD to original branch + --quit # abort rebase but do not reset HEAD + --interactive(-i) # rebase interactively with list of commits in editor + --onto?: string@"nu-complete git rebase" # starting point at which to create the new commits + --root # start rebase from root commit ] # List or change branches export extern "git branch" [ - branch?: string@"nu-complete git branches" # name of branch to operate on + branch?: string@"nu-complete git local branches" # name of branch to operate on --abbrev # use short commit hash prefixes --edit-description # open editor to edit branch description --merged # list reachable branches @@ -236,7 +277,7 @@ export extern "git branch" [ --quiet # suppress messages except errors --delete(-d) # delete branch --list # list branches - --contains: string@"nu-complete git commits" # show only branches that contain the specified commit + --contains: string@"nu-complete git commits all" # show only branches that contain the specified commit --no-contains # show only branches that don't contain specified commit --track(-t) # when creating a branch, set upstream ]