From 23e7b57875e271d9b0c427c6da8d4335168cd769 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 5 Feb 2026 11:18:32 -0800 Subject: [PATCH 1/2] git-completions: update push logic to support pushing a :delete --- custom-completions/git/git-completions.nu | 35 ++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/custom-completions/git/git-completions.nu b/custom-completions/git/git-completions.nu index dd8db204..efc0dfb8 100644 --- a/custom-completions/git/git-completions.nu +++ b/custom-completions/git/git-completions.nu @@ -382,10 +382,43 @@ export extern "git fetch" [ -6 # Use IPv6 addresses, ignore IPv4 addresses ] +# Yield local branches and (if remote is specified) remote branches with colon prefix +def "nu-complete git push" [context: string, position: int] { + use git-completion-utils * + let preceding = $context | str substring ..$position + let tokens = $preceding | str trim | args-split | where ($it not-in $GIT_SKIPABLE_FLAGS) + + # Check if we have a remote argument (2nd token, 1st is 'git', 2nd is 'push', 3rd is remote) + # BUT, args-split might be different depending on how it's called. + # "git push origin" -> ["git", "push", "origin"] + # If we have at least 3 tokens, the 3rd one IS likely the remote. + # We should double check if the 3rd token is actually a remote. + + mut remote = "" + if ($tokens | length) >= 3 { + $remote = $tokens.2 + } + + let local_branches = (nu-complete git local branches) + + if ($remote | is-empty) { + return $local_branches + } + + # If we have a remote, find branches for that remote + # Use plumbing command to get remote branches, excluding HEAD + let remote_branches = (^git for-each-ref --format='%(refname:lstrip=3)' $'refs/remotes/($remote)' | lines | where $it != 'HEAD') + + # Prefix them with : + let deletion_candidates = ($remote_branches | each { |it| $":($it)" }) + + $local_branches | append $deletion_candidates +} + # Push changes export extern "git push" [ remote?: string@"nu-complete git remotes", # the name of the remote - ...refs: string@"nu-complete git local branches" # the branch / refspec + ...refs: string@"nu-complete git push" # the branch / refspec --all # push all refs --atomic # request atomic transaction on remote side --delete(-d) # delete refs From ae1e94b72b181044e92fa6bc381870e05c3d04ac Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 5 Feb 2026 11:28:15 -0800 Subject: [PATCH 2/2] git-completions: move to for-each-ref in more places I've double-checked the behavior and found that the refactor actually fixes 2 bugs in the original implementation that I missed initially: - Symrefs: git branch output included -> target (e.g. origin/HEAD -> origin/main), which created invalid completions. for-each-ref gives clean origin/HEAD. - Detached HEAD: git branch output included (HEAD detached at ...), which was garbage. for-each-ref ignores it. I've updated the walkthrough with these details. --- custom-completions/git/git-completions.nu | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/custom-completions/git/git-completions.nu b/custom-completions/git/git-completions.nu index efc0dfb8..edaabd76 100644 --- a/custom-completions/git/git-completions.nu +++ b/custom-completions/git/git-completions.nu @@ -98,7 +98,7 @@ module git-completion-utils { } def "nu-complete git available upstream" [] { - ^git branch --no-color -a | lines | each { |line| $line | str replace '* ' "" | str trim } + ^git for-each-ref --format '%(refname:short)' refs/heads refs/remotes | lines } def "nu-complete git remotes" [] { @@ -124,12 +124,12 @@ def "nu-complete git commits current branch" [] { # Yield local branches like `main`, `feature/typo_fix` def "nu-complete git local branches" [] { - ^git branch --no-color | lines | each { |line| $line | str replace '* ' "" | str replace '+ ' "" | str trim } + ^git for-each-ref --format '%(refname:short)' refs/heads | lines } # Yield remote branches like `origin/main`, `upstream/feature-a` def "nu-complete git remote branches with prefix" [] { - ^git branch --no-color -r | lines | parse -r '^\*?(\s*|\s*\S* -> )(?P\S*$)' | get branch | uniq + ^git for-each-ref --format='%(refname:lstrip=2)' refs/remotes | lines } # Yield local and remote branch names which can be passed to `git merge`