Shell script - Bump version automatically git - linux

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

Related

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

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}"

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

How do I update my prompt to show current git branch on linux?

I am developing a bash script that adds current branch onto my Terminal prompt and shows information about the most recent commit in this folder whenever I cd into a folder that is a git repository in the terminal
Problem is that whenever I switch branches with git checkout within that repository folder the prompt does not update the current branch
this is my bash code located on my .bashrc file
cd() {
builtin cd "$#"
local status=$?
[ $status -eq 0 ] && PS1="[\e[0;32m${debian_chroot:+($debian_chroot)}\w\e[m]\e[0;35m$(parse_git_branch)\e[m \n$ "
if [ -d .git ]; then
echo -e "\nMost Recent Commit"
git show --summary;
fi
return $status
}
As documented in the Pro Git book you need the git-prompt.sh file (which should be installed as part of Git) and then in your .bashrc do something like:
. /usr/share/git-core/contrib/completion/git-prompt.sh
export GIT_PS1_SHOWDIRTYSTATE=1
export PS1='\w$(__git_ps1 " (%s)")\$ '
you have to change you ~/.bashrc and export the PS1 environment variable.
Here is an example of a ~/.bashrc:
# settings for this script
MY_DOMAIN=$(hostname -f | sed -e "s/^[^.]*\.//")
MY_FQDN=$(hostname -f)
MY_TTY=$(tty| cut -f3- -d/)
MY_USER=$(whoami)
MY_ROT="\033[31m"
MY_GRUEN="\033[32m"
MY_GELB="\033[33m"
MY_BLAU="\033[34m"
MY_LILA="\033[35m"
MY_CYAN="\033[36m"
MY_WEISS="\033[37m"
MY_FETT="\033[1m"
MY_NORMAL="\033[2m"
MY_RESET="\033[0m"
# user color
MY_U="$MY_BLAU"
case $MY_USER in
developer)
MY_U="$MY_GRUEN"
;;
root)
MY_U="$MY_ROT"
;;
esac
[ $(id -u) -eq 0 ] && MY_U="$MY_U$MY_FETT"
MY_U="\[$MY_U\]"
# host color
MY_H="$MY_ROT"
MY_H="\[$MY_H\]"
# working directory color
MY_W="\[$MY_CYAN\]"
# tty color
MY_T="\[$MY_BLAU$MY_FETT\]"
MY_R="\[$MY_RESET\]"
MY_G="\[$MY_GELB\]"
MY_GF="\[$MY_GELB$MY_FETT\]"
GIT_PS1_SHOWDIRTYSTATE=1
GIT_PS1_SHOWUNTRACKEDFILES=1
GIT_PS1_SHOWSTASHSTATE=1
GIT_PS1_SHOWUPSTREAM=verbose
export PS1="$MY_U\u$MY_R$MY_G#$MY_R$MY_H\h$MY_R$MY_G($MY_R$MY_T$MY_TTY \t$MY_R$MY_G):$MY_R$MY_W\w$MY_R\$(__git_ps1 \"$MY_GF:$MY_R \[$MY_ROT\](%s)$MY_R \")$MY_GF\$$MY_R "
which renders this:
On most linux distros you can get the branch by doing.
echo $(__git_ps1)
(develop)
If __git_ps1 unavailable, you have to source git-sh-prompt first. It may be git-prompt.sh on other distros.
source /usr/lib/git-core/git-sh-prompt
Put this one in your ~/.bash_profile file... Dynamically update your prompt with current git branch. Leave a git directory space and get a different prompt.
promptFunc() {
branch=$(git branch 2>/dev/null | grep '^*' | colrm 1 2)
if [ ! $branch ]; then
PS1=${PWD}"$ "
else
PS1="\W: "${branch}"-> "
fi
}
export PROMPT_COMMAND="promptFunc"

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

How to run a series of commands with a single command in the command line?

I typically run the following commands to deploy a particular app:
compass compile -e production --force
git add .
git commit -m "Some message"
git push
git push production master
How can I wrap that up into a single command?
I'd need to be able to customize the commit message. So the command might look something like:
deploy -m "Some message"
There are two possibilities:
a script, as others answered
a function, defined in your .bash_profile:
deploy() {
compass compile -e production --force &&
git add . &&
git commit -m "$#" &&
git push &&
git push production master
}
Without arguments, you'd have a third option, namely an alias:
alias deploy="compass compile -e production --force &&
git add . &&
git commit -m 'Dumb message' &&
git push &&
git push production master"
You could create a function that does what you want, and pass the commit message as argument:
function deploy() {
compass compile -e production --force
git add .
git commit "$#"
git push
git push production master
}
Put that in your .bashrc and you're good to go.
You can make a shell script. Something that looks like this (note no input validation etc):
#!/bin/sh
compass compile -e production --force
git add .
git commit -m $1
git push
git push production master
Save that to myscript.sh, chmod +x it, then do something like ./myscript.sh "Some message".
You can write a shell script for this
#!/bin/bash
compass compile -e production --force
git add .
git commit -m $1
git push
git push production master
Save this to 'deploy' and do a chmod 7xx on it. Now you can use it as ./deploy "Some message"
you could write these commands into a file named deploy.sh .
Then make it executable and run as sh deploy.sh
You could even add it to your path by exporting the path where you save the script.
everyone mentions about writing a script and this is probably the best way of doing it.
However you might someday want to use another way - merge commands with &&, for example:
cd ../ && touch abc
will create a file "abc" in a parent directory :)
It is just to let you know about such thing, for this particular scenario (and 99% of the others) please take a look at other answers :)
I would go through the effort of making the command work for more than just the current directory. One of the most versitle ways of doing this is to use getopt in a BASH script. Make sure you have getopt installed, create deploy.sh then chmod 755 deploy.sh and then do something like this:
#!/bin/bash
declare -r GETOPT=/usr/bin/getopt
declare -r ECHO='builtin echo'
declare -r COMPASS=/path/to/compass
declare -r GIT=/path/to/git
sanity() {
# Sanity check our runtime environment to make sure all needed apps are there.
for bin in $GETOPT $ECHO $COMPASS $GIT
do
if [ ! -x $bin ]
then
log error "Cannot find binary $bin"
return 1
fi
done
return 0
}
usage() {
$CAT <<!
${SCRIPTNAME}: Compile, add and commit directories
Usage: ${SCRIPTNAME} -e <env> [-v]
-p|--path=<path to add>
-c|--comment="Comment to add"
-e|--environment=<production|staging|dev>
Example:
$SCRIPTNAME -p /opt/test/env -c "This is the comment" -e production
!
}
checkopt() {
# Since getopt is used within this function, it must be called as
# checkopt "$#"
local SHORTOPT="-hp::c::e::"
local LONGOPT="help,path::,comment::,environment::"
eval set -- "`$GETOPT -u -o $SHORTOPT --long $LONGOPT -n $SCRIPTNAME -- $#`"
while true
do
case "$1" in
-h|--help)
return 1
;;
-|--path)
PATH="$2"
shift 2
;;
-c|--comment)
COMMENT=$2
shift 2
;;
-e|--environment)
ENV="$2"
shift 2
;;
--)
shift
break
;;
*)
$ECHO "what is $1?"
;;
esac
done
}
if ! sanity
then
die "Sanity check failed - Cant find proper system binaries"
fi
if checkopt $#
then
$ECHO "Running Compass Compile & Git commit sequence..."
$COMPASS compile -e $ENV --force
$GIT add $PATH
$GIT commit -m $COMMENT
$GIT push
$GIT push ENV master
else
usage
exit 1
fi
exit 0

Resources