Bash completion for git aliases containing multiple subcommands - bash

I have set up the following alias in my .gitconfig file:
[alias]
ss = stash show
Unfortunately bash completion does not work correctly on this alias. When I type git ss <TAB>, I get:
❯ git ss <TAB>
apply clear drop pop show
branch create list push
Which is obviously the completion for git stash instead of git stash show.
With the original command I get the list of available stashes:
❯ git stash show <TAB>
stash#{0} stash#{1}
Is there a way to get the completion on the alias behave like on the original command?
I am on Ubuntu 20.04 and using the distro's default git completions.

I posted this question on the Git mailing list and got a reply to work around this issue:
It is possible to make completion work for your particular
alias by using our completion script's extension mechanism that allows
users to specify completion functions to their own git commands. If
you type git foo <TAB> and there is a _git_foo() function in your
shell's environment, then the completion script will invoke that
function to perform completion; this works not only for commands but
for aliases as well. So for your alias you only need to "borrow" all
the "show"-subcommand-specific case arms from _git_stash() and place
them in a _git_ss() function, e.g. like this:
_git_ss () {
case "$cur" in
--*)
__gitcomp_builtin stash_show "$__git_diff_common_options"
;;
*)
__gitcomp_nl "$(__git stash list | sed -n -e 's/:.*//p')"
;;
esac
}
Add it to your ~/.bashrc, or to a separate file that you source from
your .bashrc; If you use bash-completion, then you don't even have to
touch you .bashrc: save that function to a file git-ss (dash, not
underscore!) in one of the directories scanned by bash-completion
($BASH_COMPLETION_USER_DIR, ~/.local/share/bash-completion/completions
or its XDG_DATA_HOME-equivalent) and it will be auto-loaded as needed.

Related

Create an alias with more than 1 command as the alias name

I want to create an alias that is composed of 2 commands. Could not find anything online as most questions are about an alias that equals to 2 commands, eg:
alias command="command1 && command2"
What I want:
alias git push="git add . && git commit -m 'auto ups' && git push"
Why using this instead of naming my alias gp (or whatever 1 worded alias)
I want to keep the same git push experience
Want to learn about bash
Is that possible?
As illustrated here, you would need to define a git wrapper.
It should be set in your $PATH before the /usr/local/bin/git itself.
Or you can reference that script through an alias git=git-wrapper in your ~/.bash_profile.
In that wrapper, on push, you can then call a script which would chain any git command you need.

aliases are not stored in zsh history

Whenever I execute a command using an alias, this command is not stored in the shell's command history.
So if I run history these commands do not appear in the list.
Nor do they appear when I press CTRL + r for reverse searching the command history.
When I press the keyboard's arrow up for scrolling through the last commands, I will see an aliased command only if it was the last command I ran. Other aliased commands are will not be displayed.
For example:
$ cd my-repo
$ gs # an alias to git status
$ history
Outputs the following:
2374 cd my-repo
(the gs command is not displayed)
A few notes:
gs is only an example. The issue is far more annoying in more complex commands since I have to retype them all over again instead of executing them from history (e.g. k get pods | grep <pod_name>, where k=kubectl).
gs is defined so: alias gs=' git status'.
I also have a few functions in ~/.alias, e.g.:
mkcd () {
mkdir -pv $1
For some reason, mkcd (or any other function in the alias file) is included in the history.
I do not mind if it prints out gs or expands to git status, I'll take any of the two...
I am using zsh with oh-my-zsh on macOS (Monterey). My shell aliases are defined in ~/.alias which is sourced in ~/.zshrc (source ~/.alias).
This happens both in iTerm2 and in the default Mac terminal.
Thank you for taking the time to help :-)
I will assume that your example alias is exactly what you have in your ~/.alias file.
So you have aliases like this (notice the space character in front of git command):
alias gs=' git status'
There is an shell option called HIST_IGNORE_SPACE which is doing exactly what you are experiencing - in short it will not add command to the history when it starts with space character. Example:
echo 'This command will make it to the history.'
echo 'This poor command will be forgotten.'
You can check your current options using setopt or specifically:
setopt | grep 'histignorespace'
So there are two ways how you can fix this - either by fixing your aliases to not start with space or, If you really don't want this functionality at all, by unsetting it in your ~/.zshrc like this:
unsetopt HIST_IGNORE_SPACE

Is there a way in bash/powershell to automatically include a word in a command based on context?

Currently, when inside git repos, I type git status. Is there any way for my shell to understand this is a git repo and automatically append git to relevant commands?
e.g.,
# bash example
# before
git status
# after
status # "status" isn't a registered command, but "git status" is, so bash uses the latter
I'd probably only want this enabled when I'm in a repo, but is there any way to get functionality similar to this in any of the popular shells?
I recognize this isn't always preferred or the best idea, but it could save me from typing a word.
This is what aliases were invented for in bash. You'd add a line to your bashrc that looked like this:
alias status='git status'
If you'd like, you could add one for every git subcommand you ran frequently:
alias commit='git commit'
alias checkout='git checkout'
alias add='git add'
alias push='git push'
alias pull='git pull'
alias config='git config'
Your environment is your own, and if you wanna make a shorthand for git commands, you're free to do so!
Also, these aliases will pass any arguments to the underlying command. So if you write:
commit -m "My message"
This will translate to git commit -m "My message".
These aliases should be added to a file like the .bashrc file in your home directory, if one exists.
See here for a more in-depth explanation of aliases.

How to use a line of hyphen to separate git command results within git alias in git-bash

I've already created a alias called info to display status, branch and log in git-bash. However, all the information was bunched up and somewhat annoying to read, so i wanted to make it easier on myself when reading it by adding a line of hyphens with new line above and below it. After trying many times, I can't get it to work. Therefore i'm seeking help here.
Here's what I tried before, trying to get one to work before copying it in between git branch and git log.
$ git config --global alias.info '!git status && echo && !printf -- '-%.0s' {1..80}; echo "" && echo && git branch && git log'
Below is what the result should look like.
git status result
-----------------------
git branch result
-----------------------
git log result
If the ! syntax doe not give the expected result, try and embed your commands in a shell function within your Git alias:
alias.info !f() { echo begin arg=$1/$2/end; }; f
Or even in a separate script:
git config --global alias.info '!sh info.sh'
The point is: if you can make it work in a regular shell script, you will be able to call it from an alias.
Or you can even name your script git-info (executable, no extension), and simply type:
git info
No alias needed there: any git-xxx script can be called as git xxx (if git-xxx is in your PATH)

Using !! to access bash history inside a Git alias

I want to create an alias that runs git stash and then executes the command that was before it.
This would be useful when git doesn't allow to run a command with unsaved changes, such as checkout, rebase, etc.
What I've tried:
s = "!git stash && fc -s
s = "!bash -c \"git stash && !!\""
None of the above work. It looks like in the first one git creates a subshell to run this command, as fc outputs no command found.
The second one is similar, but here I explicitly create a subshell and it obviously doesn't work, with no access to the history.
Is there a way around this? It's likely that this could be accomplished with a bash alias, but I'd prefer to do it through a git alias.
It's likely that this could be accomplished with a bash alias,
Yes, this would be simple and robust.
but I'd prefer to do it through a git alias.
Given how ugly and fragile this would be, I doubt it. However, here you go:
First, you need to relay the history. Make your shell write it out to the history file after every command by adding this to your .bashrc:
PROMPT_COMMAND='history -a'
You can then add your git alias. It needs to
run with Bash, since the system sh may not support history expansion
enable history and history expansion
read the history file
use history expansion in a separate parsing unit (e.g. after a linefeed, outside a compound command):
So all in all:
s = "!bash -c 'set -Ho history; history -r ~/.bash_history\ngit stash && !-1'"
Note that it'll run the last command executed regardless of bash instance, so if you use multiple tmux/screen/terminal windows, it won't necessarily run the last command in your current shell.
You can't use a git alias to access the Bash history (or any other shell data for that matter), since the command after the exclamation mark is not run as a subshell. Git is an external command, so all its child processes are also not subshells.
More details
You can confirm this by setting a git alias foo = !echo $$ $BASHPID and comparing it with the current shell:
$ echo $$ $BASHPID
11461 11461
$ git foo
25437
In my case it's not even running Bash. After some testing I think it's running /bin/sh, which is Dash for me since I'm using Ubuntu.

Resources