Unexpected question mark in Windows/bash - bash

I'm trying to automatically "archive" some local branches on Windows using the MinGW Bash on Windows.
I copy and pasted the content of git branch on a txt file (branches_to_be_archived.txt) and then I wrote:
A script to archive a single branch
#!/bin/bash
BRANCH_NAME=$1
echo "Branch name is $BRANCH_NAME"
git tag archive/$BRANCH_NAME $BRANCH_NAME
git branch -D $BRANCH_NAME
A script to archive every branch from a file
#!/bin/bash
FILE=./branches_to_be_archived.txt
LINES=$(cat $FILE)
for LINE in $LINES
do
./archive_branch.sh.txt $LINE
done
I inserted some echo prints and they are ok but executing git commands will raise an error like this:
fatal: Failed to resolve '[actual name of the branch]?' as a valid ref.
With a trailing ? that is not shown anywhere.
Any way to avoid having this question mark at the end?

Indeed, it was a endline characters issue (Win CRLF and Linux LF).
I solved by using Notepad++ and setting EOL -> Linux (LF) but I think that dos2unix for branches list file works just fine

Related

Basename returns file name with additional "\r\r\r" in the end?

I'm trying to write simple shell script that clones a github repo then CDs into the folder. I have
#!/bin/bash
# $1 is github repo url
url=$1
reponame=$(basename $url)
git clone $url
echo $reponame
cd $reponame
Now when I run
bash deploy_app.sh https://github.com/username/reponame
It echoes the right $reponame but the next line returns this "cd: $'reponame\r\r\r\r': No such file or directory"
I have no idea where the "\r\r\r\r" part is coming from. It's not in the repo name and it's not part of $reponame when I just echo it. Any idea why this happens??
The first thing I would be looking at is your script itself, by doing something like:
od -xcb deploy_app.sh
and looking for any carriage return characters (CR, ^M, \r, hex 0d, or octal 015) at the end of the cd line (or any line really).
That's because, if you have CR characters at the end, it will cause that problem (the ^M characters below were entered with CTRL-VCTRL-M):
pax:~> cd blah^M^M^M^M
-bash: cd: $'blah\r\r\r\r': No such file or directory
If there are CR characters in your file, you probably want to remove them, and check your line ending settings for git (assuming that file is in a repo) - this is a problem commonly caused by incorrect settings if you're updating your repo from both Windows and Unix.
If that file is not being "damaged" by git, you'll need to discover what else is putting those characters in.
And, as an aside, you wouldn't actually see whether the reponame had those characters with an echo, since echoing them would result in the actual characters, four "go to the start of the line" characters, then a "go to the next line" character. Hence there's no way to tell the difference just by looking, without pushing the output through a command like od.
I am not sure either, but try changing $reponame to "${reponame}" (with the double quotes) and see if it still happens.

git commit error: pathspec 'xxx' did not match any file(s) known to git

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 *.

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 | …

How to change the git directory delimiter?

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

How to print what a bash script is actually running?

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"

Resources