Skip to content

Commit 00f8a98

Browse files
authored
git-completion fixes (#1213)
- update push logic to support pushing a :delete Also used for-each-ref as a replacement for `branch --no-color` 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.
1 parent 049b00e commit 00f8a98

File tree

1 file changed

+37
-4
lines changed

1 file changed

+37
-4
lines changed

custom-completions/git/git-completions.nu

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ module git-completion-utils {
9898
}
9999

100100
def "nu-complete git available upstream" [] {
101-
^git branch --no-color -a | lines | each { |line| $line | str replace '* ' "" | str trim }
101+
^git for-each-ref --format '%(refname:short)' refs/heads refs/remotes | lines
102102
}
103103

104104
def "nu-complete git remotes" [] {
@@ -124,12 +124,12 @@ def "nu-complete git commits current branch" [] {
124124

125125
# Yield local branches like `main`, `feature/typo_fix`
126126
def "nu-complete git local branches" [] {
127-
^git branch --no-color | lines | each { |line| $line | str replace '* ' "" | str replace '+ ' "" | str trim }
127+
^git for-each-ref --format '%(refname:short)' refs/heads | lines
128128
}
129129

130130
# Yield remote branches like `origin/main`, `upstream/feature-a`
131131
def "nu-complete git remote branches with prefix" [] {
132-
^git branch --no-color -r | lines | parse -r '^\*?(\s*|\s*\S* -> )(?P<branch>\S*$)' | get branch | uniq
132+
^git for-each-ref --format='%(refname:lstrip=2)' refs/remotes | lines
133133
}
134134

135135
# Yield local and remote branch names which can be passed to `git merge`
@@ -382,10 +382,43 @@ export extern "git fetch" [
382382
-6 # Use IPv6 addresses, ignore IPv4 addresses
383383
]
384384

385+
# Yield local branches and (if remote is specified) remote branches with colon prefix
386+
def "nu-complete git push" [context: string, position: int] {
387+
use git-completion-utils *
388+
let preceding = $context | str substring ..$position
389+
let tokens = $preceding | str trim | args-split | where ($it not-in $GIT_SKIPABLE_FLAGS)
390+
391+
# Check if we have a remote argument (2nd token, 1st is 'git', 2nd is 'push', 3rd is remote)
392+
# BUT, args-split might be different depending on how it's called.
393+
# "git push origin" -> ["git", "push", "origin"]
394+
# If we have at least 3 tokens, the 3rd one IS likely the remote.
395+
# We should double check if the 3rd token is actually a remote.
396+
397+
mut remote = ""
398+
if ($tokens | length) >= 3 {
399+
$remote = $tokens.2
400+
}
401+
402+
let local_branches = (nu-complete git local branches)
403+
404+
if ($remote | is-empty) {
405+
return $local_branches
406+
}
407+
408+
# If we have a remote, find branches for that remote
409+
# Use plumbing command to get remote branches, excluding HEAD
410+
let remote_branches = (^git for-each-ref --format='%(refname:lstrip=3)' $'refs/remotes/($remote)' | lines | where $it != 'HEAD')
411+
412+
# Prefix them with :
413+
let deletion_candidates = ($remote_branches | each { |it| $":($it)" })
414+
415+
$local_branches | append $deletion_candidates
416+
}
417+
385418
# Push changes
386419
export extern "git push" [
387420
remote?: string@"nu-complete git remotes", # the name of the remote
388-
...refs: string@"nu-complete git local branches" # the branch / refspec
421+
...refs: string@"nu-complete git push" # the branch / refspec
389422
--all # push all refs
390423
--atomic # request atomic transaction on remote side
391424
--delete(-d) # delete refs

0 commit comments

Comments
 (0)