On Sat, 2018-06-16 at 08:36 +0200, Holger Wansing wrote:
> > The original/final lines are a bit strange, though, instead of having:
> > 
> >   if $($git foo bar); then … fi
> > 
> > I suppose it should only be:
> > 
> >   if $git foo bar; then … fi
> 
> However, with this simplified variant it fails. So I left it as is for now.

It seems there is an interesting (and new to me, or at least I'd never
fully appreciated the behaviour) corner case of the `if $(foo); then`
syntax, which is that if `foo` exits producing no output then its exit
code is apparently used for the condition. If `foo` does produce output
then the shell will attempt to execute that and use the resulting exit
code.

These just run true or false and take the output:

   $ dash -c 'if true ; then echo YES ; else echo NO ; fi'
   YES
   $ dash -c 'if false ; then echo YES ; else echo NO ; fi'
   NO

These run true or false, see the output is "" and so use the exit code:

   $ dash -c 'if $(true) ; then echo YES ; else echo NO ; fi'
   YES
   $ dash -c 'if $(false) ; then echo YES ; else echo NO ; fi'
   NO

These run `echo` (which always succeeds) then runs the resulting "true"
or "false" and uses the exit code:

   $ dash -c 'if $(echo true) ; then echo YES ; else echo NO ; fi'
   YES
   $ dash -c 'if $(echo false) ; then echo YES ; else echo NO ; fi'
   NO

This runs `echo` (which always succeeds) then tries to run the
resulting "foo" and fails because that isn't a command:

   $ dash -c 'if $(echo bar) ; then echo YES ; else echo NO ; fi'
   dash: 1: bar: not found
   NO

`git status` outputs nothing when the tree is clean, and I think the
`$($git status -s -uno $DI_COPY/packages/po)` case uses that to succeed
on a clean tree, however if the tree was dirty you'd get the "not
found" stuff for something relating to the output.

   $ git status -s -uno build/Makefile
   $ echo $?
   0
   $ dash -c 'if $(git status -s -uno build/Makefile ) ; then echo CLEAN ; else 
echo DIRTY ; fi'
   CLEAN
   $ echo "FOO" >> build/Makefile
   $ git status -s -uno build/Makefile
   M build/Makefile
   $ echo $?
   0
   $ dash -c 'if $(git status -s -uno build/Makefile ) ; then echo CLEAN ; else 
echo DIRTY ; fi'
   dash: 1: M: not found
   DIRTY

Notice that the original svn version had a `| grep -q ^C` which was
checking if any line started with a "C" (for Changed I suppose),
produced no output (`-q`) but exited with an error code reflecting the
presence of any lines. You could do something similar but you'd need to
check for more than M (modified) since git status has a variety of
error codes, including (A)dded, (D)eleted, (R)enamed etc.

`git status` doesn't seem to have an option which makes the error code
reflect the dirtiness. In the past I've used:

   # Update cache, otherwise files which have an updated
   # timestamp but no actual changes are marked as changes
   # because `git diff-index` only uses the `lstat` result and
   # not the actual file contents. Running `git update-index
   # --refresh` updates the cache.
   git update-index -q --refresh
   if git diff-index --quiet HEAD -- path/to/something ; then clean ;
   else dirty ; fi

(--quiet enable --exit-code which makes the exit status meaningful).

For perhaps less git magic you could also just write it as:
   if [ -z "$(git status -s -uno path/to/something)" ] ; then clean ; else 
dirty ; fi
or inversely:
   if [ -n "$(git status -s -uno path/to/something)" ] ; then dirty ; else 
clean ; fi

These explicitly check whether the output of the status command was
empty (the -z check, meaning clean) or non-empty (the -n check, meaning
dirty).

Ian.

Reply via email to