As part of a Capistrano Ruby task that deploys to a server, I'd like it to output the Git message and commit of the code it just deployed.
With this:
git_message = `git log -1 HEAD --pretty=format:%s`
git_commit = `git rev-parse HEAD`
git_commit = "https://example.com/myorg/myrepo/commit/" + git_commit
execute "echo \"Deployed \\\"#{git_message}\\\": #{git_commit}\" | a_command_that_posts_to_slack"
It's outputting something like this:
Deployed "Merge branch 'feature/some-feature' into develop": https://example.com/myorg/myrepo/commit/0fdfa09fbfe012649fb0a998aa2e99cb5fd7c8b3;
Note that semicolon at the very end of the commit hash. I've confirmed using puts that git_commit doesn't end with a semicolon, and git_message doesn't have one, and doesn't have one after it either.
What's adding the semicolon and how can I remove it?
It is because you have newlines in your command, which Capistrano is translating into semicolons, thinking you want to execute multiple commands (one per line).
Backticks in Ruby capture the entire stdout of the process, including trailing newlines. Use chomp to remove them.
git_message = `git log -1 HEAD --pretty=format:%s`.chomp
git_commit = `git rev-parse HEAD`.chomp
git_commit = "https://example.com/myorg/myrepo/commit/" + git_commit
execute "echo \"Deployed \\\"#{git_message}\\\": #{git_commit}\" | a_command_that_posts_to_slack"
Perhaps its executing : as the Bash shell builtin and so its attempting to terminate that command with a semi-colon? Try removing the :. I also worry about inserting double quotes as a commit message containing double quotes might cause you some headache.
Reference on the Bash builtin : What is the purpose of the : (colon) GNU Bash builtin?
Related
I wrote a bash script to update my files through git automatically, but I keep getting error for the git commit line, if my message contains error.
I have already wrapped my message with quote, what's wrong here?
My bash script:
##Handle local git update
remote='
cd express-demo-nonbare;
git pull origin master;
';
echo "Process for updating Git begin";
git add . ;
read -p "Message for this commit: " comment;
comment=\"${comment}\";
echo $comment;
git commit --message=$comment;
git push backup master;
Really you just need to quote the comment variable when supplying it to git commit. You can replace:
read -p "Message for this commit: " comment;
comment=\"${comment}\";
echo $comment;
git commit --message=$comment;
with
read -p "Message for this commit: " comment;
echo $comment;
git commit --message="$comment";
The quotes will not be part of the commit message, rather they are consumed by bash in ensuring that the entire content of the comment variable is submitted as part of a single --message=... argument to git, even if it contains whitespace characters.
You don't want to, as you put it:
wrap... my message with quote
(which is indeed what you are doing). Instead, you want to protect your message from having bash treat it as a list of words. To do that, you need quotes, but in a different position:
remote='
cd express-demo-nonbare;
git pull origin master;
'
echo "Process for updating Git begin"
git add .
read -p "Message for this commit: " comment
echo "$comment"
git commit --message="$comment"
git push backup master
I removed all non-essential semicolons as well (bash treats the end of a line as the end of the command, unless something like an unclosed parenthesis or brace prevents this).
It's not clear to me what your intent is in setting the variable remote to the literal string newlinecdspaceex...;newline, especially since $remote does not occur later in the script. Note, however, that since this string does include white-space, expanding it outside quotes, as in $remote (vs "$remote" which expands it inside quotes) can trigger further shell actions. For instance:
foo='this; that'
wc $foo
will have the wc program attempt to open files named this; and that, having split $foo at the white spaces into separate words that are then passed to wc. This splitting is actually based on $IFS:
IFS=+
foo='this+that'
wc $foo
tries to open files named this and that. Restoring IFS to its normal setting:
wc $foo
tries to open one file named this+that.
(I sometimes use wc as a program to help show what the actual arguments were, since it tries to open each one as a file name, and spits out the actual file name in any subsequent error message or count.)
Similarly, if the expansion of a variable produces shell glob metacharacters, these will be evaluated after the expansion:
foo='*'
wc $foo
will try to open and read every file and directory in the current directory. Again, double quotes will protect against this:
wc "$foo"
will only try to open and read one file, named *.
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 | …
When working inside a windows command prompt, all of my paths indicate director separators with a backslash \, when using GIT commands, all of the paths are instead using forwardslash /. How do I change GIT's output to mirror my command line output?
Example inconsistent directory indicators;
D:\git\demo>git status --s
A test/subdir/foo.txt
How do I change GIT's output to mirror my command line output?
First, all git commands are executed in a git bash sub-shell, which explains why you see '/'.
A '\' is an escape character for a bash session, which is why it is not used in any bash command output.
You would need to use a git wrapper (a git.pat set in your PATH) in order to replace any / by \.
git.bat:
C:\prgs\git\latest\bin\git.exe %*|C:\prgs\git\latest\usr\bin\sed.exe -e 's:/:\\\\:'
Make sure git.bat is set before git.exe in your %PATH%: type where git to check the order in which git(s) are discovered.
And replace C:\prgs\git\latest by the path your Git is installed.
By specifying the full path for git.exe and for sed.exe, you are sure to use the right executable.
Since what you're looking for seems to be not specifically "how do I make Git use \ in file paths" but rather "how do I make Git generate file paths with \", you can pipe the output through sed (which is packaged in Git Bash) like so:
$ git status --s | sed 's/\//\\/g'
M dir\file.py
?? dir\input001.txt
?? dir\output001.txt
and to avoid typing sed every single time you can configure a Git alias to do it for you:
[alias]
ws = "!ws() { : git status ; git status --short $# | sed 's/\\//\\\\/g' ; } && ws"
which will let you do this:
$ git ws
M dir\file.py
?? dir\input001.txt
?? dir\output001.txt
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
I am trying to make a bash script which will:
Add all changes to git
Commit with a message I pass to the bash script
Push it to the repo
I am trying to do this with:
m=\"$*\"
git add -A
echo git commit -m $m
git push
However, I am getting errors saying error: pathspec 'Q2,' did not match any file(s) known to git. for everything word I pass to the script.
How can I see what bash it actually doing? When I put echo in front of the offending line (which I presume is the commit) I get a correctly form command. If I put it in to the terminal, I runs find.
To see what bash is doing, add -xv options to the shebang line:
#!/bin/bash -xv
The problem is probably the quoting. m=\"$*\" does not do what you want. $m is still split into several words if it contains whitespace, just the first word starts with a doublequote and the last word ends in a doublequote.
Rather, change the offending line to
git commit -m "$m"