I'm trying to use test in a make target, specifically to check for changes to certain files with git (I don't think that particular git diff-index HEAD -- command makes a difference here).
is_diff:
if [[ -n `git diff-index HEAD --` ]]; exit 1; fi
This works fine as a bash script, but as a make target it seems to be dependent on which version of make is running, which limits its utility (and makes just pointing to a shell script version better).
Is there a change that would make this a little more make-version independent?
Use [ -n "`git diff-index HEAD --`" ] instead of the bash-specific [[ -n ... ]]
was the correct answer from a comment by Renaud Pacalet.
Also as MadScientist notes, that the original question omitted then so
if [ -n "`git diff-index HEAD --`" ]; then exit 1; fi
Related
How can I run a command in bash or any other bash-like shell only if another command outputs something?
I am working on an API that updates the cache storage of a tool and commits the updated cache to GitHub. What I want to do is, after rebuilding the cache, I want to check if git diff HEAD ./path/to/cache outputs something and if it does, then I want to run git add ./path/to/cache.
Unfortunately, this doesn't work:
git diff HEAD ./path/to/cache && git add ./path/to/cache
I think because the first command doesn't return false or exit and instead outputs an empty string or nothing at all. So, what's the correct way to achieve this in bash?
Use the --exit-code option, which causes git diff to exit with 0 if there are no diffs and 1 otherwise. Then you can ignore the output.
git diff --exit-code HEAD ./path/to/cache || git add ./path/to/cache
The answer posted by #chepner is definitely the best for my use case. But my question was originally for any use case. If you want to achieve the same behavior for any other commands, you can use the grep command to check if the first command outputs anything.
git diff HEAD ./path/to/cache | grep -q . && git add ./path/to/cache
[ -z $(git diff HEAD ./path/to/cache) ] || git add ./path/to/cache
# [ EXPRESSION ], -z the length of STRING is zero.
# || If no, execute subsequent shell
I recently moved from bash to zsh, and like most of people I had my custom bash aliases/functions to ease git and env sourcing operations. In particular there are 2 of them which doesn't work properly when run on zsh but work completely fine on bash.
export REPO_ROOT=/home/pablo/repos/my_repo
alias croot='cd $REPO_ROOT'
alias subroot='cd $REPO_ROOT/subrepo/subrepo_1/'
repcheckout(){
git checkout "$1"
if [ $(pwd) == $REPO_ROOT ]; then
subroot
else
croot
fi
git checkout "$1"
if [ $(pwd) == $REPO_ROOT ]; then
subroot
else
croot
fi
}
The idea is that I have a set of main_repo-submodule branches and when I checkout the main repo, I want to checkout the submodule in the corresponding branch, instead of doing:
$ git submodule update --init --recursive subrepo/subrepo_1
which checkouts the proper commit in the submodule but doesn't update that I switched to a certain local branch.
For the previous func, the error dropped by zsh when running
$ repcheckout my_cool_branch
is
M subrepo/subrepo_1/
Switched to branch 'my_cool_branch'
repcheckout:2: = not found
Later I have a setup.sh file that I source which goes as follows:
add2path() {
if ! echo ${!1} | egrep "(^|:)$2(:|\$)" > /dev/null ; then
declare -g $1="${!1}:$2"
export "$1"
fi
}
# GENERATED BINARY A
export BIN_A_HOME="$REPO_ROOT/bin_a"
add2path PATH "$BIN_A_HOME/bin"
Same with some generated python modules that are added to PYTHONPATH using the same add2path
Which drops the error:
add2path:1: bad substitution
Both functions use bashisms that aren't valid in zsh.
In repcheckout, the problem is using the == operator in a [ ] test -- the standard operator is =, but bash allows == as a synonym; zsh doesn't. I'd also recommend double-quoting both strings to avoid problems with weird characters in the path (and maybe using "$PWD" instead of $(pwd)):
if [ "$PWD" = "$REPO_ROOT" ]; then
In add2path, the problem is the indirect variable reference ${!1} in both the echo and declare commands. zsh also allows indirect variable references, but its syntax is completely different: ${(P)1}. You could probably make a cross-compatible version with eval, but that tends to cause weird bugs if you don't use it exactly right; I'd just rewrite the function as needed for zsh.
EDIT: If you want to use the same code under both bash and zsh, eval is probably better than trying to detect which shell you're in and using conditional code based on that. Here's a quick stab at writing a cross-shell compatible version:
if ! eval "echo \"\$$1\"" | egrep "(^|:)$2(:|\$)" > /dev/null ; then
eval "declare -g $1=\"\$$1:\$2\""
export "$1"
...
The quoting is ugly, but it should work ok as long as $1 contains a valid identifier; if it doesn't, the usual eval problems may rear their ugly heads.
I have a bash script that gets updated fairly often that I would like to have self-update only itself using git, but not affect anything else.
I found an Example Script that updates itself, but it uses git pull --force which updates everything. Most of the time this should be fine, but I hesitate to automatically do something with the potential to have unintended consequences, safer to just affect only itself.
My attempts to modify that script to use checkout or cherry-pick have not been successful.
Does anyone have a function that updates only $0 or can write one?
Edit:
This is the messy code I wrote for my script.
#!/bin/bash
BRANCH="master"
SCRIPTNAME=$1
REPOSITORY="https://stash.xxx/projects/IT/repos/xxx/browse/$SCRIPTNAME"
self_update() {
git fetch
if [[ -n $(git diff --name-only origin/$BRANCH | grep $SCRIPTNAME) ]]
then
echo The version you have and the version on stash are different
echo
echo Do you want to:
echo
echo s. Show messy differences
echo c. Open repository in Chrome
echo
echo d. Download the stash version, overwrite your current version, and exit script
echo
echo q. return to the previous menu
read choice
case $choice in
s)
git diff origin/$BRANCH
echo
read -p "Enter to Return " enterkey
;;
c)
open -a "/Applications/Google Chrome.app" "$REPOSITORY"
;;
d)
git checkout -f origin/$BRANCH -- $SCRIPTNAME
#head -5 $SCRIPTNAME
exit
;;
q)
break
;;
*)
echo Please enter one of the choices.
;;
esac
else
echo You are using the current version of $SCRIPTNAME
break
fi
}
#testing code
head -5 $SCRIPTNAME
while :
do
self_update
done
head -5 $SCRIPTNAME
Checkout should work
git fetch the-remote
git checkout the-remote/the-branch -- the-file.sh
This better not run on windows because it will reject rewrite the script while it's running.
Due to some problems with a script which commits and pushes automatically, i'd like to implement a whitelist.
The plan is, that only commits with the pattern 'foo' and 'bar' in path, are allowed.
#!/bin/sh
WHITELIST="foo bar"
WRKDIR=/home/athur/workwork/test/repo
cd $WRKDIR
git add -A
for file in `git diff --cached -p --name-status | cut -c3-`; do
if [[ "$file" == *"$WHITELIST"* ]] ; then
echo "$file is on whitelist"
else
echo "$file is not on whitelist. Commit aborted."
exit 1
fi
done
The problem is, it's always uses the 'else' clause.
I can't find the problem. Thanks
As a best-practices approach, consider:
#!/usr/bin/env bash
# ^^^^ important: [[ ]] is not guaranteed to work with bin/sh
whitelist_re='(foo|bar)'
workdir=/home/athur/workwork/test/repo
cd -- "$workdir" || exit
git add -A
while IFS= read -r filename; do
if [[ $file =~ $whitelist ]]; then
echo "$file is on whitelist" >&2
else
echo "$file is not on whitelist; commit aborted." >&2
exit 1
fi
done < <(git diff --cached --name-only)
To walk through the changes:
The shebang specifies bash as a shell, which guarantees that extensions like [[ ]] and <(...) will be available -- a guarantee not made with /bin/sh.
A while read loop is used rather than attempting to iterate over line-oriented data with for; see DontReadLinesWithFor for an explanation of the reasoning behind this change.
The whitelist is specified as an ERE-compliant regular expression, such that =~ can be used to test whether a value matches.
Instead of using git diff --cached --name-status and then using cut to remove the status data after-the-fact, we use --name-only to generate only names in the first place.
Using lowercase variable names complies with the conventions given in http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html, specifying that POSIX-defined tools will use all-caps shell and environment variable names for their own purposes, and that names with at least one lowercase character are reserved for application use. (Keep in mind that setting a shell variable overwrites any like-named environment variable, so these conventions apply even when export is not in use).
By the way, if you just wanted to find out if any non-matches exist, without knowing which files those are, you could use:
#!/bin/sh
# ^^ actually safe here, as no non-POSIX functionality is used
whitelist_re='foo|bar'
if git diff --cached --name-only | grep -qEv "$whitelist_re"; then
echo "At least one file is not on whitelist; commit aborted" >&2
exit 1
fi
Using an explicit list
The == is not symmetric in this case and ** seems to be used badly.
Try "$WHITELIST" == *"$file"*.
(Inspired by How do I check if a variable exists in a list in BASH)
Note that using your WHITELIST, only files foo and bar will be whitelisted.
Detecting a pattern
If you need to detect individual patterns, you may need to construct a function such as:
for entry in $WHITELIST ; do
if [[ "$file" =~ $entry ]] ; then
return 0
fi
done
return 1
How can I check if 2 revisions in SVN differs?
I only want to know if they have the same content or not.
I know you check the diff command. Example
svn diff -r 12345:67890 http://subversion.test.com/svn/myapp
But this can produce a lot of output.
The return code of diff is always zero, no matter the outcome.
I am using linux /bash
I want to incoorperate this in a form like:
svn diff -r 12345:67890 http://subversion.test.com/svn/myapp
if [[ "$?" == "1" ]]; then
echo "Code is Different"
fi
You could just use:
mod=`svn diff -r 12345:67890 http://subversion.test.com/svn/myapp | wc -l`
if [[ $mod -ne 0 ]]; then
echo "Code is Different"
fi
Since the svn diff should list difference.
You can play around with telling svn to use an arbitrary diff command, using svn diff --diff-cmd some-diff-cmd.
The low-tech solution could be checking out both revisions and running your /usr/bin/diff -q on them; that will yield a different exit status for differing and identical files.