In Bash Script Second Argument to Function is not Processed as Expected - linux

Trying to combine git - add/commit/push in one fuction.
gitacp testfile/testfile.py "testing" works okay.
gitacp testfile/testfile.py "testing this" results in this error.
error: pathspec 'this' did not match any file(s) known to git.
From some reason the set of strings in the second argument is not
getting processed correctly.
# Git add, commit and push
function gitacp {
args=("$#")
filepathname=${args[0]}
comment=${args[1]}
branchname=$(git status 2>/dev/null | head -n1 | cut -d" " -f3)
git add ${filepathname}
git commit -m ${comment}
echo ${branchname}
echo ${branchnmrmspc}
echo ${comment}
echo ${filepathname}
git push --set-upstream origin ${branchname}
}
gitacp testfile/testfile.py "testing this" results in this error.
error: pathspec 'this' did not match any file(s) known to git.

Quote the argument
git commit -m "${comment}"

Related

Verifying multiple directories exist on their appropriate branches

I need to create a new Makefile that sources the master Makefile, and then uses the variables defined within to check if the directories exist in their appropriate local branches. I've read a lot of posts on StackOverflow about checking if directories exists, but I'm stuck on how to find out if their in the appropriate branches.
#!bin/ksh
DIRLOC=/var/tmp
DIRNAMES="SchemaExtract SQL Count SchExtArchive"
for DIRNAME in ${DIRNAMES}
do
if [ -d ${DIRLOC}/${DIRNAME} ]
then
echo ${DIRLOC}/${DIRNAME} already exists
else
echo ${DIRLOC}/${DIRNAME} Creating ...
mkdir ${DIRLOC}/${DIRNAME}
chmod 755 ${DIRLOC}/${DIRNAME}
fi
done
Any help would be appreciated!
Clarification-
I want to specify in my new Makefile what git branch each directory is supposed to be in. So I need a code that reads the directories from the master Makefile, checks if they exist and if so, compare the location of the directories found with the locations that I specify in the new Makefile to determine everything is in its correct git branch.
You can use the git ls-tree command to check for a directories existence in a given branch.
As an example, consider the following repository:
# There are 3 branches.
$ git branch
branch1
branch2
* master
# master contains master_dir
$ ls
master_dir
# branch1 contains master_dir and branch1_dir
$ git checkout branch1
Switched to branch 'branch1'
$ ls
branch1_dir master_dir
# branch2 contains master_dir and branch2_dir
$ git checkout branch2
Switched to branch 'branch2'
$ ls
branch2_dir master_dir
# switch back to the master branch
$ git checkout master
Switched to branch 'master'
$ ls
master_dir
The following commands are run from the master branch.
For branch1:
$ git ls-tree -d branch1:branch1_dir
$ git ls-tree -d branch1:branch2_dir
fatal: Not a valid object name branch1:branch2_dir
For branch2:
$ git ls-tree -d branch2:branch2_dir
$ git ls-tree -d branch2:branch1_dir
fatal: Not a valid object name branch2:branch1_dir
In your shell script, you can use the return value of the command in your conditional:
$ git ls-tree -d branch1:branch1_dir 2&> /dev/null; \
> if [[ $? -eq 0 ]]; then echo "Exists"; else echo "Does not exist"; fi
Exists
$ git ls-tree -d branch1:branch2_dir 2&> /dev/null; \
> if [[ $? -eq 0 ]]; then echo "Exists"; else echo "Does not exist"; fi
Does not exist
EDIT: Example shell script using directory definitions in an external file.
$ cat branch-dirs.txt
branch1:branch1_dir
branch2:branch2_dir
branch2:non_existent_dir
$ cat check_dirs.sh
#!/bin/bash
readonly BRANCH_DIR_FILE="./branch-dirs.txt"
for dir_to_check in $(cat "$BRANCH_DIR_FILE"); do
git ls-tree -d "${dir_to_check}" 2&> /dev/null
if [[ $? -eq 0 ]]; then
echo "${dir_to_check} exists."
else
echo "${dir_to_check} does not exist."
fi
done
$ ./check_dirs.sh
branch1:branch1_dir exists.
branch2:branch2_dir exists.
branch2:non_existent_dir does not exist.
So I was browsing through and came across this post. Wouldn't this work a little better for what I need it to do in the long run since I need it to work from the top-level down?
MY_DIRNAME=../External
ifneq "$(wildcard $(MY_DIRNAME) )" ""
# if directory MY_DIRNAME exists:
INCLUDES += -I../External
else
# if it doesn't:
INCLUDES += -I$(HOME)/Code/External
endif

Shell script - Bump version automatically git

I've the following command which I want to execute with one command via 'makefile' how can I do it ?
1. git tag -a v0.0.1 -m "new release"
2. git push origin v0.0.1
Now I've created something for start
git:
git add .
git commit -m "$m"
git push origin master
Now I've two issue, how to resolve the version e.g.
Here is v0.0.1 but for each new release I need to bump it like first is
v0.0.1 and the next release should be v0.0.2, can it be done somehow automatically (maybe have some counter...)? if not maybe add it as parameter to one command
git tag -a v0.0.1 -m "new release"
git push origin v0.0.1
update
There is answer which looks good with the following
git describe --tags --abbrev=0 | awk -F. '{$NF+=1; OFS="."; print $0}'
but How should I combine it with ?
git tag -a v0.0.1 -m "new release"
git push origin v0.0.1
update 2
When I try the following as suggest in Kevin answer I got error:
.PHONY: git
VERSION=git describe --tags --abbrev=0 | awk -F. '{$NF+=1; OFS="."; print $0}'
git:
git add .
git commit -m "$m"
git push origin master
git tag -a $(VERSION) -m "new release"
git push origin $(VERSION)
The error is: fatal: tag 'ERSION' already exists
it seems that the bump of not working and it somehow remove the v from version
I did another check, remove the repo and start it from scratch manually for the first release 0.0.1 now I did change in one file and run the script , the version should now be 0.0.2 if it success, but no Im getting error fatal: tag 'v0.0.1' already exists which explain that the bump is not working, any idea why ?
I guess it's related to this code `'{$NF+=1; OFS="."; print $0}'
Using the last pushed tag you could automatically increment your version number:
git describe --tags --abbrev=0 | awk -F. '{OFS="."; $NF+=1; print $0}'
Keep in mind that you store it in a variable and use it to tag and push:
VERSION=`git describe --tags --abbrev=0 | awk -F. '{OFS="."; $NF+=1; print $0}'`
git tag -a $VERSION -m "new release"
git push origin $VERSION
Explanation:
git describe - Show the most recent tag that is reachable from a commit
--tags - Enables matching a lightweight (non-annotated) tag.
--abbrev=0 - Will suppress long format, only showing the closest tag.
awk -F. - Process pattern using "." as a delimiter
'{OFS="."; $NF+=1; print $0}' - only increment last number and join with "."
makefile:
.PHONY: git
git:
$(eval VERSION=$(shell git describe --tags --abbrev=0 | awk -F. '{OFS="."; $$NF+=1; print $0}'))
git add .
git commit -m "$m"
git push origin master
git tag -a $(VERSION) -m "new release"
git push origin $(VERSION)
Based on Kevin Sandow's answer, find a more elaborate shell script below
latest version : https://gist.github.com/acucchieri/69bc649abde55315fe74bb68be82e0c8
#!/bin/bash
# The bump is performed only on the "main" or "master" branch unless a branch is specified with the -b argument
# Example :
# bump-version -b staging
# Check that HEAD is not detached
DETACHED=`git branch --show-current | wc -l`
if [ $DETACHED -eq 0 ]; then
echo "HEAD is detached. Please fix it before."
exit 1
fi
BUILD_BRANCH=''
# Check if a branch was passed as an argument
while getopts "b:" option
do
case $option in
b)
BUILD_BRANCH=$OPTARG
;;
esac
done
# Determines the build branch ("main" or "master") if no branch was passed as an argument
if [ -z "$BUILD_BRANCH" ]; then
if [ `git rev-parse --verify main 2>/dev/null` ]
then
BUILD_BRANCH='main'
else
if [ `git rev-parse --verify master 2>/dev/null` ]
then
BUILD_BRANCH='master'
else
echo "Unable to find \"main\" or \"master\" branch. Please use -b arg"
exit 1
fi
fi
fi
# Check that local is not behind origin
git fetch 2>/dev/null
if [ "$(git rev-list --count HEAD..$BUILD_BRANCH)" -gt 0 ]; then
echo "Local is behind Origin. Please run git pull first."
exit 1
fi
# Guess the next tag
if [[ "$(git tag --merged $BUILD_BRANCH)" ]]; then
# increment the last tag
NEXT_TAG=`git describe --tags --abbrev=0 | awk -F. '{OFS="."; $NF+=1; print $0}'`
else
# there is no tag yet
NEXT_TAG='0.1.0'
fi
# Ask for next tag
SEMVER_REGEX="^[vV]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?(\\+[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?$"
SEMVER_VALID=false
while [[ $SEMVER_VALID == false ]]
do
read -p "Next tag [$NEXT_TAG]: " TAG
# empty answer
if [ -z "$TAG" ]; then
# set guessed tag
TAG=$NEXT_TAG
fi
# semver validation
if [[ "$TAG" =~ $SEMVER_REGEX ]]; then
SEMVER_VALID=true
else
echo 'Tag must match the semver scheme X.Y.Z[-PRERELEASE][+BUILD]. See https://semver.org/'
fi
done
# Release message
if [[ $TAG =~ ^[v] ]]; then
# remove "v" letter
MESSAGE="release ${TAG:1:${#TAG}-1}"
else
MESSAGE="release $TAG"
fi
# Checks if a commit is needed
if [ -n "$(git status --porcelain)" ]; then
git add -A .
git commit -am "bump version"
fi
git tag -a "$TAG" -m "$MESSAGE"
# Ask to push new release
read -p "Push new release (Y/n)? [Y]:" -r
REPLY=${REPLY:-Y}
if [[ $REPLY =~ ^[YyOo]$ ]]; then
git push origin $BUILD_BRANCH --follow-tags
fi
exit 0

Execute line given in output of another command

Warning: this question is not about usage of git, but about usage of pipes in Linux, git command is given here as example.
Having such output of git push
fatal: The current branch my_branch has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin my_branch
I want to execute given command, i.e. git push --set-upstream origin my_branch.
By doing git push 2>&1 | tail -3 I get git push --set-upstream origin my_branch printed on screen.
Question: What command should be added to next pipe so given git push will be executed. So I could do git push 2>&1 | tail -3 | command_to_eval_given_string
Just pipe to bash itself:
git push ... | bash
This will send the stdout from the previous pipes to bash, that will then execute it.
$ echo "echo 'hello'" | bash
hello
$ echo "uptime" | bash
16:22:37 up 7:31, 3 users, load average: 0,03, 0,14, 0,23
Rather than piping to another command, you can instead wrap the pipeline in a command substitution and eval it. This will execute the command in your current shell session. For example:
eval "$(printf 'some output 1\nsome output 2\necho execute me'| tail -1)";
## execute me

Find a specific folder in all remote and local GIT branches

I have few hundreds of remote and local branches. I wonder whether there is a command to help me find a folder with a specific name in all branches.
My git version is 1.8.3.1. I also have smartgit installed if it matters.
Thanks in advance.
The following command will output all refs (local and remotes) that point to a commit which contains the path specified in the variable SEARCH_PATH
SEARCH_PATH="somePath"
git for-each-ref --format="%(refname)" refs/heads refs/remotes |
while read ref
do
if [[ `git ls-tree -r --name-only $ref` =~ "$SEARCH_PATH" ]] ; then
echo $ref;
fi
done
You can run following to list your required folders/files
for line in `git for-each-ref --format="%(refname)" refs/heads`;
do
git ls-tree -r $line | grep 'file_regex'
done

Can't add a file separated with space to git

I have been writing a script to add untracked files using git add .
The loop I use in my script is
for FILE in $(git ls-files -o --exclude-standard); do
git add $FILE
git commit -m "Added $FILE"
git push origin master
done
The script runs fine till it faces a filename which has space in it. for Eg., I cant add the file Hello 22.mp4.(Note that there is a SPACE between Hello and 22). The above loop would take the file as 2 separate files, Hello and 22.mp4 and exit with error.
Does someone know how to add it as a single file?
Thanks
What's happening is the shell is expanding the $(...) into a bunch of words, and it's obviously interpreting a file with spaces embedded as multiple files obviously. Even with the prior suggestions of quoting the git add command, it wouldn't work. So the loop is getting run with wrong arguments, as shown by this output with set -x:
ubuntu#up:~/test$ ls -1
a a
ubuntu#up:~/test$ set -x; for FILE in $(git ls-files -o --exclude-standard); do git add "$FILE"; git commit -m "Added $FILE"; done
+ set -x
++ git ls-files -o --exclude-standard
+ for FILE in '$(git ls-files -o --exclude-standard)'
+ git add a
...
The proper solution is to quote the git add $file and have git ls-files NULL separate the filenames by passing -z to git ls-files and use a while loop with a null delimiter:
git ls-files -o --exclude-standard -z | while read -r -d '' file; do
git add "$file"
git commit -m "Added $file"
git push origin master
done
If you are using bash alternative to the solution provided by #AndrewF, you can make use of IFS bash internal variable to change the delimiter from space to newline, something on these lines:
(IFS=$'\n'
for FILE in $(git ls-files -o --exclude-standard); do
git add $FILE
git commit -m "Added $FILE"
git push origin master
done
)
This is just for your information. The response of AndrewF is more informative covering debugging option & usage of while instead of for.
Hope this helps!
Try putting the $FILE var in quotes:
git add "$FILE"
That'll quote the filename, thus allowing spaces in it.
Replace git add $FILE with git add "$FILE". That way it will be interpreted as a single element.
I know that this is very late but here is one way to do it using the standard xargs linux command:
git ls-files -o --exclude-standard | xargs -L 1 -I{} -d '\n' git add '{}'
You can test it by simply echoing the command as follows:
git ls-files -o --exclude-standard | xargs -L 1 -I{} -d '\n' echo "git add '{}'"
To add as a single file add a backslash before the space in the filename:
git add pathtofilename/filenamewith\ space.txt

Resources