Cannot get the exact bash prompt I want - bash

What's throwing me off mostly is trying to get new lines as I want. And trust me, I've googled and searched through this site, but can't get exactly what I want. I either get parsing errors or not exactly what I want.
What I want is this
/c/what/ever/folder/here
(gitbranch)
$ STARTTYPING HERE
first line green, second yellow, third white. What I've been able to come up with gives me this:
/c/what/ever/folder/here
(gitbranch) $ STARTTYPING HERE
note the extra space before (gitbranch) and lack of newline before the "$"
here is my PS1 so far:
parse_git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}
PS1="\[\033[32m\]\w\[\033[33m\]\n\$(parse_git_branch)\[\033[00m\] $ "
I'm wanting this because we sometimes have long branch and directory names and hate having my prompt start anywhere but to the far left

I think involving sed is generating some output that is really confusing the expansions inside the double-quoted string you're using for PS1.
You could switch to splicing special strings, single-quoted strings, and double quoted ones together (e.g. "$foo"'"not $expanded'$'\n') if you like, but that's a bit confusing.
Instead, why not skip parsing git branch's output at all, and just ask for the branch directly?
Assuming that you want the branch name (second line) not to show up at all when not in a Git repository, this works:
branch=$(git rev-parse --abbrev-ref HEAD 2> /dev/null)
PS1="\[\033[32m\]\w${branch:+\[\033[33m\]\n($branch)}\[\033[00m\]\n$ "
The magic is in the :+ parameter expansion statement: it assigns/returns everything on the right of the plus if the left side is non-empty; nothing otherwise. Think of it a bit like the $cond ? $trueval : $falseval ternary operator in other programming languages.
If you want a default string instead of no second line, try:
PS1="\[\033[32m\]\w\[\033[33m\]\n(${branch:-no branch detected})\[\033[00m\]\n$ "
When you're in a brand new repo, but you haven't committed anything yet, that code will prompt that you're in branch HEAD. If you'd like it to say master or something else, do something like this:
branch=$(git rev-parse --abbrev-ref HEAD 2> /dev/null)
if [ "$branch" = "HEAD" ]; then
branch="master, brand new"
fi
You're probably already doing this, but if you want your prompt to update when you change directories, running that code once won't work; it'll "stick" with whatever the git branch of where you first ran it was. To make that happen, plug that code into your PROMPT_COMMAND bash variable via a function, like so:
prompt_with_git() {
branch=$(git rev-parse --abbrev-ref HEAD 2> /dev/null)
PS1="\[\033[32m\]\w${branch:+\[\033[33m\]\n($branch)}\[\033[00m\]\n$ "
}
PROMPT_COMMAND=prompt_with_git
Once you've sourced that, things should work.
Note that this requires a Git that supports the rev-parse command and arguments used here. Do git rev-parse --abbrev-ref HEAD in a committed repo folder manually to check if yours does; if there's an error, you may need to upgrade your Git.

Related

"git push origin master" gives weird behavior in bash script

I have this simple function in my bashrc file
function aupgrade {
cat ~/.bash_aliases > ~/bash/.bash_aliases
cd ~/bash
git add .
if [[ $1 == "" ]]; then
git commit -m "Update"
else
git commit -m "$1"
fi
git push origin master
cd - 1>/dev/null
}
This function has a purpose, this is the expected behavior:
First, replace the content of the .bash_aliases file in the bash repository with the stdout of cat ~/.bash_aliases
Second, go into the ~/bash directory which is a git respository
Third, stage all changes
Fourth, if when calling the aupgrade function the following argument is nothing, just commit with the "Update" message, but if the user wrote an argument, like aupgrade "New commit!", commit the changes with such argument as the message, git commit -m $1
Fifth, push the changes
Sixth, go back to the previous directory
BUT, it doesnt do that, instead it just does:
First, replace the content of the .bash_aliases file in the bash repository with the stdout of cat ~/.bash_aliases
Second, go into the ~/bash directory which is a git respository
Third, stage all changes
Fourth, commit with the "Update" message although there is an argument
Fifth, push the changes
Sixth, go back to the previous directory
This is weird. This looks product of the git push origin master line. It's not a conditional problem, because when I wrote another function like this but without the git push origin master line it worked as desired.
Why does this happen? Is there any solution?
This is what set -x shows to me, it's really weird
So I have tested your code, and the only problem I could find is that passing your first argument as a string with multiple exclamation marks inserts the same (or previous?) command unless you escape it:
It looks like this is exactly what happened for you, but you had "set -x" ran beforehand, thus it replaced the double exclamation marks with that command.
I'd also recommend storing function arguments in named local variables for cleaner code.

Bash alias to wrap around Git commit

I am trying to write a bash alias to wrap around a Git commit command.
Here is what my typical Git commit looks like.
git commit -am 'Comments in here'
Here is what I have attempted to write as an alias (which would go inside my .bashrc file), so I don't have to write out the whole command every time.
comm(){
git commit -am $1
}
Then I would call it like this: comm 'Comments in here'
However I keep getting this error: fatal: Paths with -a does not make sense.
Anything I'm missing here?
Use More Quotes™ - $1 is being split into words (which have a special meaning in *nix shell scripts). So any words except the first one in your commit message is treated as a filename, which indeed does not make sense with -a.
The -a (automatically add) option is causing the error. If you use it with a path on the git command, you get errors.
The -m option requires an argument, the message, so you either need to make your function manage two arguments or change how you use the function.
To provide a generic git-checkin-with-message function, I'd use something like this:
gcim() {
git commit -m "${1:?'Missing git commit message!'}"
}
Then use it just like git commit -m. For example:
gcim "My cool commit does really good stuff"
Adding files to the commit is almost always better done beforehand. git -a will happily add all files if you happen to use . as an argument. The only thing protecting git commit -a from inserting unwanted files is a properly constructed .gitignore file.
However, if you sometimes want to do a git -a -m "some msg" command, you can make another function:
gciam() {
local msg="${1:?Missing commit message!}"
shift
if (( $# > 0 )) ; then
git commit -m "$msg" $*
else
echo "Missing files to add!"
fi
}
Then, to use:
gciam "added new file" that_file.rb

Output of a git command read through shell script is adding special characters when passed again to another git command

I have a requirement to delete branches in git repository of a developer who recently left the organization. So I used git for-each-ref to list all the branches then used grep to filter results by developer name (test_developer in the following script). I used shell read and extracted branch into a variable mybr and used git push origin --delete to delete the branch. Please find the code snippet below:
git for-each-ref --format='%(align:1,left)%(color:yellow)%(authorname)%(end) %(color:reset)%(refname:strip=3)' --sort=authorname refs/remotes | grep test_developer | while read line;do mystr=(${line}); mybr=${mystr[1]}; git push origin --delete "$mybr"; done
Issue is am getting output as "fatal: remote part of refspec is not a valid name in :?[mbugfix/CRIP-2475". Here bugfix/CRIP-2475 is the branch name. And I wonder what are those extra characters :?[m that got appended before the branch name.
If I do echo $mybr before git delete I get the value properly printed as "bugfix/CRIP-2475". And if I pass this value manually to git delete, it is working fine. But when it is being passed as a variable, am getting the above error. I suspect there are some special characters being prepended, may be a ctrlM character or something that echo is not printing to the screen.
Is there anyway to remove those extra characters?
Yes you can remove by using Bash sub-string replacement
${str/#find/replace} for replace pre-fix characters
${str/%find/replace} for replace post-fix characters
git for-each-ref --format='%(align:1,left)%(color:yellow)%(authorname)%(end) %(color:reset)%(refname:strip=3)' --sort=authorname refs/remotes | grep test_developer | while read line;do mystr=(${line}); mybr=${mystr[1]}; git push origin --delete "${mybr/#?[m/}"; done
These "magical" symbols are, of course, colors. You shoudn' use colors in pipes:
git for-each-ref --format='%(align:1,left)%(authorname)%(end) %(refname:strip=3)' --sort=authorname refs/remotes | …

Shell script: escaping list of parameters including hash symbol (#)

I wrote a custom shell script for myself to make it easier for me to commit code to Github.
Recently, I wanted to start using Github's ability to automatically close issues by including their number in the commit message:
# Would automatically close #1 on push
git add .
git commit -m "Closes issue #1 ..."
git push
However, the way my script is set up, it grabs all the parameters using $* but that automatically removes anything after the # symbol, because that's a comment in shell scripts.
commit() {
# Print out commands for user to see
echo "=> git add ."
echo "=> git commit -m '$*'"
echo "=> git push --set-upstream origin $current_branch"
# Actually execute commands
git add .
git commit -m "$*"
git push --set-upstream origin $current_branch
}
Now I CAN do commit 'Closes issue #1 ...' with the wrapping quotes, but that's slightly annoying...I specifically setup my script so I could easily write: commit Whatever message I want to put in here...
I've looked over the man pages and done some SO searching, but I can't find anything about the specific issue of escaping # symbols as a parameter.
Is this even possible?
Anything after the # is interpreted by the shell as a comment, so it will not passed to the function. This happens before the function is executed. There is nothing the function can do to prevent this.
There are two canonical ways of doing this:
Just require quoting. Every single Unix tool that accepts command line parameters requires this.
Have the program read the commit from stdin instead with read -r input. You can then run just commit and type in the message.
These are both good solutions because they're simple, familiar, transparent, robust, idiomatic Unix that is straight forward to reason about. It's always better to work with Unix than against it.
However, if you instead prefer a complex, unfamiliar, opaque, fragile special case, you can kludge it with magic aliases and history:
commit() {
echo "You wrote: $(HISTTIMEFORMAT= history 1 | cut -d ' ' -f 2-)"
}
alias commit="commit # "
Here's an example of this:
$ commit This is text with #comments and mismatched 'quotes and * and $(expansions)
You wrote: commit This is text with #comments and mismatched 'quotes and * and $(expansions)
just played a little with it
the script
commit() {
echo "$*"
}
script's usage and output
➜ ~ commit "whatever you want #1 for some reason"
whatever you want #1 for some reason
➜ ~ commit 'whatever you want #1 for some reason'
whatever you want #1 for some reason
➜ ~ commit whatever you want \#1 for some reason
whatever you want #1 for some reason
➜ ~ commit whatever you want #1 for some reason
whatever you want
➜ ~
so if you don't want to quote the message you need to escape the hash with a \ (backslash), that is actually a regular escape character

How do I assign the value of grep into a variable and concatenate with a string?

I have a couple of servers on my development machine. I have a script each to run them and I am working on a few git branches at a time. So whenever I am running the script that runs the server, I want it to print which server from which branch of git I am trying to run in a different color.
Here is what I have but it is not working properly.
git_branch= git branch|grep '*'|cut -c3-
echo -e "\e[1;33;40m Running API server on git branch $git_branch \e[0m"
...
REST_OF_THE_CODE_THAT_RUNS_THE_DEV_SERVER
...
It prints the git branch first and the value does not seem to be assigned to the variable at all. I am not able to figure out where I am going wrong
To assign the result of a command line to a variable, use backquotes or $(..) notation. For example, assuming your command line works, you could use:
git_branch=$(git branch|grep '*'|cut -c3-)
This notation is preferable to backquotes because it's more easily readable, and allows nesting without the need to escape things:
echo $(echo foo$(echo bar))
Note that in your original code, your grep should be more specific. Remember that an asterisk really means "zero or more of the previous atom", to grep. So this would be better:
git_branch=$(git branch|grep '^[*]'|cut -c3-)

Resources